From: Jaroslav Kysela Date: Mon, 8 May 2000 18:53:38 +0000 (+0000) Subject: Merged pcm2 branch. X-Git-Tag: v1.0.3~1276 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=1cd6778173f3fe770e4dac7cff557adcc6336aac;p=alsa-lib.git Merged pcm2 branch. --- diff --git a/acinclude.m4 b/acinclude.m4 index b94cb802..fd99d697 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -16,7 +16,7 @@ void main(void) #if !defined(SND_PROTOCOL_VERSION) || !defined(SND_PROTOCOL_INCOMPATIBLE) #error not found #else -#if !defined(SND_MIXER_IOCTL_ELEMENTS) +#if !defined(SND_PCM_IOCTL_CHANNEL_UPDATE) #error wrong version #endif exit(0); @@ -25,7 +25,7 @@ void main(void) ], AC_MSG_RESULT(present), [AC_MSG_RESULT(not found or wrong version); - AC_MSG_ERROR([Install alsa-driver v0.3.0pre5+ package first...])] + AC_MSG_ERROR([Install alsa-driver v0.6.0 package first...])] ) CFLAGS="$OLD_CFLAGS" ]) diff --git a/cvscompile b/cvscompile index 4a09e51f..5c5cccd2 100644 --- a/cvscompile +++ b/cvscompile @@ -3,7 +3,7 @@ aclocal $ACLOCAL_FLAGS automake --foreign autoconf -export CFLAGS='-O2 -Wall -pipe -g' +export CFLAGS='-O2 -Wall -W -pipe -g' echo "CFLAGS=$CFLAGS" echo "./configure $@" ./configure $@ diff --git a/include/footer.h b/include/footer.h index 45ef8269..d09558ac 100644 --- a/include/footer.h +++ b/include/footer.h @@ -1,3 +1,5 @@ +#define UNUSED __attribute__ ((unused)) + /* * */ diff --git a/include/instr.h b/include/instr.h index 73c835db..b071e616 100644 --- a/include/instr.h +++ b/include/instr.h @@ -13,8 +13,8 @@ typedef void snd_instr_simple_t; extern "C" { #endif -int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_seq_instr_put_t **put, long *size); -int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *data, long size, snd_instr_simple_t **simple); +int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_seq_instr_put_t **put, size_t *size); +int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *data, size_t size, snd_instr_simple_t **simple); int snd_instr_simple_free(snd_instr_simple_t *simple); #ifdef __cplusplus @@ -35,8 +35,8 @@ int snd_instr_iwffff_open_rom(snd_iwffff_handle_t **handle, int card, int bank, int snd_instr_iwffff_open_rom_file(snd_iwffff_handle_t **handle, const char *name, int bank, int file); int snd_instr_iwffff_close(snd_iwffff_handle_t *handle); int snd_instr_iwffff_load(snd_iwffff_handle_t *handle, int bank, int prg, snd_instr_iwffff_t **iwffff); -int snd_instr_iwffff_convert_to_stream(snd_instr_iwffff_t *iwffff, const char *name, snd_seq_instr_put_t **data, long *size); -int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data, long size, snd_instr_iwffff_t **iwffff); +int snd_instr_iwffff_convert_to_stream(snd_instr_iwffff_t *iwffff, const char *name, snd_seq_instr_put_t **data, size_t *size); +int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data, size_t size, snd_instr_iwffff_t **iwffff); int snd_instr_iwffff_free(snd_instr_iwffff_t *iwffff); #ifdef __cplusplus diff --git a/include/pcm.h b/include/pcm.h index 40b5097a..6eb060b3 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -8,26 +8,97 @@ #define SND_PCM_OPEN_PLAYBACK 0x0001 #define SND_PCM_OPEN_CAPTURE 0x0002 #define SND_PCM_OPEN_DUPLEX 0x0003 -#define SND_PCM_OPEN_NONBLOCK 0x1000 +#define SND_PCM_NONBLOCK_PLAYBACK 0x1000 +#define SND_PCM_NONBLOCK_CAPTURE 0x2000 +#define SND_PCM_NONBLOCK 0x3000 #ifdef __cplusplus extern "C" { #endif +typedef unsigned int bitset_t; + +static inline size_t bitset_size(int nbits) +{ + return (nbits + sizeof(bitset_t) * 8 - 1) / (sizeof(bitset_t) * 8); +} + +static inline bitset_t *bitset_alloc(int nbits) +{ + return calloc(bitset_size(nbits), sizeof(bitset_t)); +} + +static inline void bitset_set(bitset_t *bitmap, unsigned int pos) +{ + int bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] |= 1 << (pos % bits); +} + +static inline void bitset_reset(bitset_t *bitmap, unsigned int pos) +{ + int bits = sizeof(*bitmap) * 8; + bitmap[pos / bits] &= ~(1 << (pos % bits)); +} + +static inline int bitset_get(bitset_t *bitmap, unsigned int pos) +{ + int bits = sizeof(*bitmap) * 8; + return !!(bitmap[pos / bits] & (1 << (pos % bits))); +} + +static inline void bitset_copy(bitset_t *dst, bitset_t *src, unsigned int nbits) +{ + memcpy(dst, src, bitset_size(nbits) * sizeof(bitset_t)); +} + +static inline void bitset_and(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ &= *bs++; +} + +static inline void bitset_or(bitset_t *dst, bitset_t *bs, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ |= *bs++; +} + +static inline void bitset_zero(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = 0; +} + +static inline void bitset_one(bitset_t *dst, unsigned int nbits) +{ + bitset_t *end = dst + bitset_size(nbits); + while (dst < end) + *dst++ = -1; +} + typedef struct snd_pcm snd_pcm_t; typedef struct snd_pcm_loopback snd_pcm_loopback_t; +typedef enum { SND_PCM_TYPE_HW, SND_PCM_TYPE_PLUG } snd_pcm_type_t; + int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode); int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode); + +snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle); int snd_pcm_close(snd_pcm_t *handle); +int snd_pcm_channel_close(snd_pcm_t *handle, int channel); int snd_pcm_file_descriptor(snd_pcm_t *handle, int channel); -int snd_pcm_nonblock_mode(snd_pcm_t *handle, int nonblock); -int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t * info); -int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t * info); -int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t * params); -int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t * setup); -int snd_pcm_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t * setup); -int snd_pcm_channel_status(snd_pcm_t *handle, snd_pcm_channel_status_t * status); +int snd_pcm_channel_nonblock(snd_pcm_t *handle, int channel, int nonblock); +int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info); +int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info); +int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params); +int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup); +int snd_pcm_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup); +int snd_pcm_all_voices_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t *setup); +int snd_pcm_channel_status(snd_pcm_t *handle, snd_pcm_channel_status_t *status); int snd_pcm_playback_prepare(snd_pcm_t *handle); int snd_pcm_capture_prepare(snd_pcm_t *handle); int snd_pcm_channel_prepare(snd_pcm_t *handle, int channel); @@ -36,17 +107,34 @@ int snd_pcm_capture_go(snd_pcm_t *handle); int snd_pcm_channel_go(snd_pcm_t *handle, int channel); int snd_pcm_sync_go(snd_pcm_t *handle, snd_pcm_sync_t *sync); int snd_pcm_playback_drain(snd_pcm_t *handle); +int snd_pcm_channel_drain(snd_pcm_t *handle, int channel); int snd_pcm_playback_flush(snd_pcm_t *handle); int snd_pcm_capture_flush(snd_pcm_t *handle); int snd_pcm_channel_flush(snd_pcm_t *handle, int channel); int snd_pcm_playback_pause(snd_pcm_t *handle, int enable); +int snd_pcm_channel_pause(snd_pcm_t *handle, int channel, int enable); ssize_t snd_pcm_transfer_size(snd_pcm_t *handle, int channel); ssize_t snd_pcm_write(snd_pcm_t *handle, const void *buffer, size_t size); ssize_t snd_pcm_read(snd_pcm_t *handle, void *buffer, size_t size); -ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count); -ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count); +ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); +ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); int snd_pcm_mmap(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control, void **buffer); int snd_pcm_munmap(snd_pcm_t *handle, int channel); +int snd_pcm_mmap_control(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control); +int snd_pcm_mmap_data(snd_pcm_t *handle, int channel, void **buffer); +int snd_pcm_munmap_control(snd_pcm_t *handle, int channel); +int snd_pcm_munmap_data(snd_pcm_t *handle, int channel); +int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask); +int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags); +int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags); +int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *bytes); +int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *bytes); +int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel); +ssize_t snd_pcm_mmap_write(snd_pcm_t *handle, const void *buffer, size_t size); +ssize_t snd_pcm_mmap_read(snd_pcm_t *handle, void *buffer, size_t size); +ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); +ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); +ssize_t snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel); /* misc */ @@ -59,7 +147,12 @@ int snd_pcm_format_width(int format); /* in bits */ int snd_pcm_format_physical_width(int format); /* in bits */ int snd_pcm_build_linear_format(int width, int unsignd, int big_endian); ssize_t snd_pcm_format_size(int format, size_t samples); -unsigned char snd_pcm_format_silence(int format); +ssize_t snd_pcm_format_bytes_per_second(snd_pcm_format_t *format); +u_int8_t snd_pcm_format_silence(int format); +u_int16_t snd_pcm_format_silence_16(int format); +u_int32_t snd_pcm_format_silence_32(int format); +u_int64_t snd_pcm_format_silence_64(int format); +ssize_t snd_pcm_format_set_silence(int format, void *buf, size_t count); const char *snd_pcm_get_format_name(int format); #ifdef __cplusplus @@ -82,6 +175,7 @@ typedef enum { PREPARE = 1, DRAIN = 2, FLUSH = 3, + PAUSE = 4, } snd_pcm_plugin_action_t; typedef struct snd_stru_pcm_plugin_voice { @@ -89,25 +183,31 @@ typedef struct snd_stru_pcm_plugin_voice { void *addr; /* address to voice samples */ unsigned int first; /* offset to first sample in bits */ unsigned int step; /* samples distance in bits */ + unsigned int enabled:1; /* voice need to be processed */ + unsigned int wanted:1; /* voice is wanted */ } snd_pcm_plugin_voice_t; struct snd_stru_pcm_plugin { char *name; /* plug-in name */ + int channel; 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); - int (*dst_voices)(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_voice_t **voices, - size_t samples); + int (*client_voices)(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices); + int (*src_voices_mask)(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask); + int (*dst_voices_mask)(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask); ssize_t (*transfer)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples); int (*action)(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action, @@ -123,49 +223,32 @@ struct snd_stru_pcm_plugin { 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_voice_t *src_voices; + snd_pcm_plugin_voice_t *dst_voices; + bitset_t *src_vmask; + bitset_t *dst_vmask; + char extra_data[0]; }; -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_plug_connect(snd_pcm_t **handle, snd_pcm_t *slave, int mode, int close_slave); +int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode); +int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int mode); + 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); -int snd_pcm_plugin_append(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin); -int snd_pcm_plugin_remove_to(snd_pcm_t *handle, int channel, snd_pcm_plugin_t *plugin); -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_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); -int snd_pcm_plugin_voice_setup(snd_pcm_t *handle, int channel, snd_pcm_voice_setup_t * setup); -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); -int snd_pcm_plugin_mmap(snd_pcm_t *handle, int channel, snd_pcm_mmap_control_t **control, void **buffer); -int snd_pcm_plugin_munmap(snd_pcm_t *handle, int channel); -int snd_pcm_plugin_go(snd_pcm_t *handle, int channel); -int snd_pcm_plugin_sync_go(snd_pcm_t *handle, snd_pcm_sync_t *sync); -int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable); -int snd_pcm_plugin_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup); +int snd_pcm_plug_clear(snd_pcm_t *handle, int channel); +int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin); +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); +#if 0 +int snd_pcm_plugin_remove_to(snd_pcm_plugin_t *plugin); +int snd_pcm_plug_remove_first(snd_pcm_t *handle, int channel); +#endif +snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_t *handle, int channel); +snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_t *handle, int channel); +int snd_pcm_plug_direct(snd_pcm_t *pcm, int channel); +ssize_t snd_pcm_plug_client_samples(snd_pcm_t *handle, int channel, size_t drv_samples); +ssize_t snd_pcm_plug_slave_samples(snd_pcm_t *handle, int channel, size_t clt_samples); +ssize_t snd_pcm_plug_client_size(snd_pcm_t *handle, int channel, size_t drv_size); +ssize_t snd_pcm_plug_slave_size(snd_pcm_t *handle, int channel, size_t clt_size); /* * Plug-In helpers @@ -175,91 +258,75 @@ ssize_t snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_t *plugin, size_t samp 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 */ +int snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle, + int channel, + const char *name, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int extra, + snd_pcm_plugin_t **ret); /* basic I/O */ -int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *handle, int channel, +int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *handle, + int channel, + snd_pcm_t *slave, 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, +int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *handle, + int channel, + snd_pcm_t *slave, 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, +int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *handle, + int channel, + snd_pcm_t *slave, snd_pcm_format_t *format, snd_pcm_plugin_t **r_plugin); /* conversion plugins */ int snd_pcm_plugin_build_interleave(snd_pcm_plugin_handle_t *handle, + int channel, 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_plugin_handle_t *handle, + int channel, 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_plugin_handle_t *handle, + int channel, 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_plugin_handle_t *handle, + int channel, 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, + int channel, 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_plugin_handle_t *handle, + int channel, 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_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, float *ttable, snd_pcm_plugin_t **r_plugin); - -#ifdef __cplusplus -} -#endif - -/* - * Loopback interface - */ - -#define SND_PCM_LB_OPEN_PLAYBACK 0 -#define SND_PCM_LB_OPEN_CAPTURE 1 - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct snd_pcm_loopback_callbacks { - void *private_data; /* should be used with an application */ - size_t max_buffer_size; /* zero = default (64kB) */ - void (*data) (void *private_data, char *buffer, size_t count); - void (*position_change) (void *private_data, unsigned int pos); - void (*format_change) (void *private_data, snd_pcm_format_t *format); - void (*silence) (void *private_data, size_t count); - void *reserved[31]; /* reserved for the future use - must be NULL!!! */ -} snd_pcm_loopback_callbacks_t; - -int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subdev, int mode); -int snd_pcm_loopback_close(snd_pcm_loopback_t *handle); -int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *handle); -int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *handle, int enable); -int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *handle, int mode); -int snd_pcm_loopback_format(snd_pcm_loopback_t *handle, snd_pcm_format_t * format); -int snd_pcm_loopback_status(snd_pcm_loopback_t *handle, snd_pcm_loopback_status_t * status); -ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *handle, snd_pcm_loopback_callbacks_t * callbacks); +int snd_pcm_plugin_build_copy(snd_pcm_plugin_handle_t *handle, + int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin); #ifdef __cplusplus } diff --git a/include/seq.h b/include/seq.h index e38011e4..764cf486 100644 --- a/include/seq.h +++ b/include/seq.h @@ -22,8 +22,8 @@ int snd_seq_block_mode(snd_seq_t *handle, int enable); int snd_seq_client_id(snd_seq_t *handle); int snd_seq_output_buffer_size(snd_seq_t *handle); int snd_seq_input_buffer_size(snd_seq_t *handle); -int snd_seq_resize_output_buffer(snd_seq_t *handle, int size); -int snd_seq_resize_input_buffer(snd_seq_t *handle, int size); +int snd_seq_resize_output_buffer(snd_seq_t *handle, size_t size); +int snd_seq_resize_input_buffer(snd_seq_t *handle, size_t size); int snd_seq_system_info(snd_seq_t *handle, snd_seq_system_info_t *info); int snd_seq_get_client_info(snd_seq_t *handle, snd_seq_client_info_t *info); int snd_seq_get_any_client_info(snd_seq_t *handle, int client, snd_seq_client_info_t *info); @@ -64,7 +64,7 @@ int snd_seq_query_next_port(snd_seq_t *handle, snd_seq_port_info_t * info); /* event routines */ snd_seq_event_t *snd_seq_create_event(void); int snd_seq_free_event(snd_seq_event_t *ev); -int snd_seq_event_length(snd_seq_event_t *ev); +ssize_t snd_seq_event_length(snd_seq_event_t *ev); int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev); int snd_seq_event_output(snd_seq_t *handle, snd_seq_event_t *ev); int snd_seq_event_output_buffer(snd_seq_t *handle, snd_seq_event_t *ev); diff --git a/src/Makefile.am b/src/Makefile.am index e7d085e9..152dcb3d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr COMPATNUM=@LIBTOOL_VERSION_INFO@ lib_LTLIBRARIES = libasound.la -libasound_la_SOURCES = error.c +libasound_la_SOURCES = error.c libasound_la_LIBADD = control/libcontrol.la mixer/libmixer.la pcm/libpcm.la \ rawmidi/librawmidi.la timer/libtimer.la hwdep/libhwdep.la \ seq/libseq.la instr/libinstr.la diff --git a/src/error.c b/src/error.c index b96ed57d..47fb5ec8 100644 --- a/src/error.c +++ b/src/error.c @@ -38,7 +38,7 @@ const char *snd_strerror(int errnum) if (errnum < SND_ERROR_BEGIN) return (const char *) strerror(errnum); errnum -= SND_ERROR_BEGIN; - if (errnum >= sizeof(snd_error_codes) / sizeof(const char *)) + if ((unsigned int) errnum >= sizeof(snd_error_codes) / sizeof(const char *)) return "Unknown error"; return snd_error_codes[errnum]; } diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index 22e6001c..42af50cd 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -132,7 +132,7 @@ ssize_t snd_hwdep_write(snd_hwdep_t *hwdep, const void *buffer, size_t size) { ssize_t result; - if (!hwdep || (!buffer && size > 0) || size < 0) + if (!hwdep || (!buffer && size > 0)) return -EINVAL; result = write(hwdep->fd, buffer, size); if (result < 0) @@ -144,7 +144,7 @@ ssize_t snd_hwdep_read(snd_hwdep_t *hwdep, void *buffer, size_t size) { ssize_t result; - if (!hwdep || (!buffer && size > 0) || size < 0) + if (!hwdep || (!buffer && size > 0)) return -EINVAL; result = read(hwdep->fd, buffer, size); if (result < 0) diff --git a/src/instr/iwffff.c b/src/instr/iwffff.c index bac54912..bf2cb8b6 100644 --- a/src/instr/iwffff.c +++ b/src/instr/iwffff.c @@ -174,7 +174,7 @@ struct envelope_record { struct snd_iwffff_handle { int rom; unsigned char *fff_data; - unsigned int fff_size; + size_t fff_size; char *fff_filename; char *dat_filename; unsigned int start_addr; @@ -237,7 +237,7 @@ int snd_instr_iwffff_open(snd_iwffff_handle_t **handle, const char *name_fff, co close(fd); return -ENOMEM; } - if (read(fd, iwf->fff_data, iwf->fff_size) != iwf->fff_size) { + if (read(fd, iwf->fff_data, iwf->fff_size) != (ssize_t) iwf->fff_size) { free(iwf->fff_data); free(iwf); close(fd); @@ -292,7 +292,7 @@ int snd_instr_iwffff_open_rom(snd_iwffff_handle_t **handle, int card, int bank, close(fd); return -ENOMEM; } - if (read(fd, iwf->fff_data, ffff.length) != ffff.length) { + if (read(fd, iwf->fff_data, ffff.length) != (ssize_t) ffff.length) { free(iwf->fff_data); free(iwf); close(fd); @@ -386,7 +386,7 @@ int snd_instr_iwffff_free(snd_instr_iwffff_t *__instr) return 0; } -static char *look_for_id(snd_iwffff_handle_t *iwf, unsigned char *start, +static char *look_for_id(snd_iwffff_handle_t *iwf UNUSED, unsigned char *start, unsigned char *end, ID id) { if (!start) @@ -485,7 +485,7 @@ static int load_iw_wave(snd_iwffff_handle_t *file, close(fd); return -ENOMEM; } - if (read(fd, result, size) != size) { + if (read(fd, result, size) != (ssize_t) size) { free(*result); *result = NULL; close(fd); @@ -832,9 +832,9 @@ int snd_instr_iwffff_conv_to_stream(snd_instr_iwffff_t *iwffff, return 0; } -int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data, - long size, - snd_instr_iwffff_t **iwffff) +int snd_instr_iwffff_convert_from_stream(snd_seq_instr_get_t *data UNUSED, + size_t size UNUSED, + snd_instr_iwffff_t **iwffff UNUSED) { /* TODO */ return -ENXIO; diff --git a/src/instr/simple.c b/src/instr/simple.c index 0d4d0385..1033692a 100644 --- a/src/instr/simple.c +++ b/src/instr/simple.c @@ -43,7 +43,7 @@ static long simple_size(simple_instrument_t *instr) int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, const char *name, snd_seq_instr_put_t **__data, - long *__size) + size_t *__size) { snd_seq_instr_put_t *put; snd_seq_instr_data_t *data; @@ -93,9 +93,9 @@ int snd_instr_simple_convert_to_stream(snd_instr_simple_t *simple, return 0; } -int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *__data, - long size, - snd_instr_simple_t **simple) +int snd_instr_simple_convert_from_stream(snd_seq_instr_get_t *__data UNUSED, + size_t size UNUSED, + snd_instr_simple_t **simple UNUSED) { /* TODO */ return -ENXIO; diff --git a/src/mixer/elements.c b/src/mixer/elements.c index b4431e69..ef3462f6 100644 --- a/src/mixer/elements.c +++ b/src/mixer/elements.c @@ -91,15 +91,6 @@ int snd_mixer_element_info_build(snd_mixer_t *handle, snd_mixer_element_info_t * if ((err = snd_mixer_element_info(handle, element)) < 0) return err; break; - case SND_MIXER_ETYPE_SWITCH3: - element->data.switch3.voices_size = element->data.switch3.voices_over; - element->data.switch3.voices = element->data.switch3.voices_over = 0; - element->data.switch3.pvoices = (snd_mixer_voice_t *)malloc(element->data.switch3.voices_size * sizeof(snd_mixer_voice_t)); - if (!element->data.switch3.pvoices) - return -ENOMEM; - if ((err = snd_mixer_element_info(handle, element)) < 0) - return err; - break; case SND_MIXER_ETYPE_VOLUME1: element->data.volume1.range_size = element->data.volume1.range_over; element->data.volume1.range = element->data.volume1.range_over = 0; @@ -176,9 +167,6 @@ int snd_mixer_element_info_free(snd_mixer_element_info_t *element) case SND_MIXER_ETYPE_PLAYBACK1: safe_free((void **)&element->data.pcm1.pdevices); break; - case SND_MIXER_ETYPE_SWITCH3: - safe_free((void **)&element->data.switch3.pvoices); - break; case SND_MIXER_ETYPE_VOLUME1: safe_free((void **)&element->data.volume1.prange); break; diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index 65079e00..4a636a3d 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -32,7 +32,7 @@ #include #define SND_FILE_MIXER "/dev/snd/mixerC%iD%i" -#define SND_MIXER_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 1) +#define SND_MIXER_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) struct snd_mixer { int card; @@ -335,7 +335,7 @@ void snd_mixer_sort_gid(snd_mixer_gid_t *list, int count, /* Compare using name and index */ int snd_mixer_compare_gid_name_index(const snd_mixer_gid_t *a, const snd_mixer_gid_t *b, - void* ignored) + void *ignored UNUSED) { int r = strcmp(a->name, b->name); if (r != 0) @@ -426,7 +426,7 @@ void snd_mixer_sort_eid(snd_mixer_eid_t *list, int count, /* Compare using name and index */ int snd_mixer_compare_eid_name_index(const snd_mixer_eid_t *a, const snd_mixer_eid_t *b, - void* ignored) + void *ignored UNUSED) { int r = strcmp(a->name, b->name); if (r != 0) diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index 4bc4473e..6820111f 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -2,8 +2,8 @@ SUBDIRS = plugin EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = pcm.c pcm_plugin.c pcm_plugin_build.c pcm_misc.c \ - pcm_loopback.c +libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_plugin_build.c pcm_misc.c \ + pcm_mmap.c libpcm_la_LIBADD = plugin/libpcmplugin.la noinst_HEADERS = pcm_local.h diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 24694ad3..aae4d02a 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -1,7 +1,7 @@ /* * PCM Interface - main file * Copyright (c) 1998 by Jaroslav Kysela - * + * Copyright (c) 2000 by Abramo Bagnara * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as @@ -20,567 +20,521 @@ */ #include -#include -#include -#include +#include #include -#include -#include -#include +#include +#include #include "pcm_local.h" -#define SND_FILE_PCM_PLAYBACK "/dev/snd/pcmC%iD%ip" -#define SND_FILE_PCM_CAPTURE "/dev/snd/pcmC%iD%ic" -#define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) - -int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode) +int snd_pcm_abstract_open(snd_pcm_t **handle, int mode, + snd_pcm_type_t type, size_t extra) { - return snd_pcm_open_subdevice(handle, card, device, -1, mode); -} + snd_pcm_t *pcm; -static int snd_pcm_open_channel(int card, int device, int channel, int subdevice, int fmode, snd_ctl_t *ctl, int *ver) -{ - char filename[32]; - char *filefmt; - int err, fd; - int attempt = 0; - snd_pcm_channel_info_t info; - switch (channel) { - case SND_PCM_CHANNEL_PLAYBACK: - filefmt = SND_FILE_PCM_PLAYBACK; - break; - case SND_PCM_CHANNEL_CAPTURE: - filefmt = SND_FILE_PCM_CAPTURE; - break; - default: - return -EINVAL; - } - if ((err = snd_ctl_pcm_channel_prefer_subdevice(ctl, device, channel, subdevice)) < 0) - return err; - sprintf(filename, filefmt, card, device); - - __again: - if (attempt++ > 3) { - snd_ctl_close(ctl); - return -EBUSY; - } - if ((fd = open(filename, fmode)) < 0) { - err = -errno; - return err; - } - if (ioctl(fd, SND_PCM_IOCTL_PVERSION, ver) < 0) { - err = -errno; - close(fd); - return err; - } - if (SND_PROTOCOL_INCOMPATIBLE(*ver, SND_PCM_VERSION_MAX)) { - close(fd); - return -SND_ERROR_INCOMPATIBLE_VERSION; + if (!handle) + return -EFAULT; + *handle = NULL; + + pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t) + extra); + if (pcm == NULL) + return -ENOMEM; + if (mode & SND_PCM_OPEN_PLAYBACK) { + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + chan->open = 1; + chan->mode = (mode & SND_PCM_NONBLOCK_PLAYBACK) ? SND_PCM_NONBLOCK : 0; } - if (subdevice >= 0) { - memset(&info, 0, sizeof(info)); - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &info) < 0) { - err = -errno; - close(fd); - return err; - } - if (info.subdevice != subdevice) { - close(fd); - goto __again; - } + if (mode & SND_PCM_OPEN_CAPTURE) { + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + chan->open = 1; + chan->mode = (mode & SND_PCM_NONBLOCK_CAPTURE) ? SND_PCM_NONBLOCK : 0; } - return fd; + pcm->type = type; + pcm->mode = mode & SND_PCM_OPEN_DUPLEX; + *handle = pcm; + return 0; } -int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode) +snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle) { - int fmode, ver, err; - snd_pcm_t *pcm; - snd_ctl_t *ctl; - int pfd = -1, cfd = -1; + return handle->type; +} - *handle = NULL; - - if (card < 0 || card >= SND_CARDS) +int snd_pcm_channel_close(snd_pcm_t *pcm, int channel) +{ + int ret = 0; + int err; + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if ((err = snd_ctl_open(&ctl, card)) < 0) - return err; - fmode = O_RDWR; - if (mode & SND_PCM_OPEN_NONBLOCK) - fmode |= O_NONBLOCK; - if (mode & SND_PCM_OPEN_PLAYBACK) { - pfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_PLAYBACK, - subdevice, fmode, ctl, &ver); - if (pfd < 0) { - snd_ctl_close(ctl); - return pfd; - } + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (chan->mmap_control) { + if ((err = snd_pcm_munmap_control(pcm, channel)) < 0) + ret = err; } - if (mode & SND_PCM_OPEN_CAPTURE) { - cfd = snd_pcm_open_channel(card, device, SND_PCM_CHANNEL_CAPTURE, - subdevice, fmode, ctl, &ver); - if (cfd < 0) { - if (pfd >= 0) - close(pfd); - snd_ctl_close(ctl); - return cfd; - } + if (chan->mmap_data) { + if ((err = snd_pcm_munmap_data(pcm, channel)) < 0) + ret = err; } - snd_ctl_close(ctl); - if (pfd < 0 && cfd < 0) - return -EINVAL; - pcm = (snd_pcm_t *) calloc(1, sizeof(snd_pcm_t)); - if (pcm == NULL) { - if (pfd >= 0) - close(pfd); - if (cfd >= 0) - close(cfd); - return -ENOMEM; + if ((err = pcm->ops->channel_close(pcm, channel)) < 0) + ret = err; + chan->open = 0; + chan->valid_setup = 0; + if (chan->valid_voices_setup) { + chan->valid_voices_setup = 0; + free(chan->voices_setup); } - pcm->card = card; - pcm->device = device; - pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd; - pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd; - pcm->mode = mode; - pcm->ver = ver; - *handle = pcm; - return 0; -} + return ret; +} int snd_pcm_close(snd_pcm_t *pcm) { - int res = 0; + int err, ret = 0; int channel; if (!pcm) - return -EINVAL; + return -EFAULT; for (channel = 0; channel < 2; ++channel) { - snd_pcm_plugin_munmap(pcm, channel); - snd_pcm_plugin_clear(pcm, channel); - snd_pcm_munmap(pcm, channel); - if (pcm->chan[channel].fd >= 0) - if (close(pcm->chan[channel].fd)) - res = -errno; + if (pcm->chan[channel].open) { + if ((err = snd_pcm_channel_close(pcm, channel)) < 0) + ret = err; + } } free(pcm); - return res; + return ret; } -int snd_pcm_file_descriptor(snd_pcm_t *pcm, int channel) +int snd_pcm_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock) { + int err; if (!pcm) - return -EINVAL; + return -EFAULT; if (channel < 0 || channel > 1) return -EINVAL; - return pcm->chan[channel].fd; -} - -int snd_pcm_nonblock_mode(snd_pcm_t *pcm, int nonblock) -{ - long flags; - int fd, channel; - - if (!pcm) - return -EINVAL; - for (channel = 0; channel < 2; ++channel) { - fd = pcm->chan[channel].fd; - if (fd < 0) - continue; - if ((flags = fcntl(fd, F_GETFL)) < 0) - return -errno; - if (nonblock) - flags |= O_NONBLOCK; - else - flags &= ~O_NONBLOCK; - if (fcntl(fd, F_SETFL, flags) < 0) - return -errno; - if (nonblock) - pcm->mode |= SND_PCM_OPEN_NONBLOCK; - else - pcm->mode &= ~SND_PCM_OPEN_NONBLOCK; - } + if (!pcm->chan[channel].open) + return -EBADFD; + if ((err = pcm->ops->channel_nonblock(pcm, channel, nonblock)) < 0) + return err; + if (nonblock) + pcm->chan[channel].mode |= SND_PCM_NONBLOCK; + else + pcm->chan[channel].mode &= ~SND_PCM_NONBLOCK; return 0; } -int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) { - int fd, channel; if (!pcm || !info) - return -EINVAL; - for (channel = 0; channel < 2; ++channel) { - fd = pcm->chan[channel].fd; - if (fd >= 0) - break; - } - if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0) - return -errno; - return 0; + return -EFAULT; + return pcm->ops->info(pcm, info); } -int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { - int fd; if (!pcm || !info) - return -EINVAL; + return -EFAULT; if (info->channel < 0 || info->channel > 1) return -EINVAL; - fd = pcm->chan[info->channel].fd; - if (fd < 0) - return -EINVAL; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) - return -errno; - return 0; + if (!pcm->chan[info->channel].open) + return -EBADFD; + return pcm->ops->channel_info(pcm, info); } -int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) { int err; - int fd; + snd_pcm_channel_setup_t setup; struct snd_pcm_chan *chan; - if (!pcm || !params) - return -EINVAL; + return -EFAULT; if (params->channel < 0 || params->channel > 1) return -EINVAL; chan = &pcm->chan[params->channel]; - fd = chan->fd; - if (fd < 0) - return -EINVAL; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) - return -errno; - chan->setup_is_valid = 0; - memset(&chan->setup, 0, sizeof(snd_pcm_channel_setup_t)); - chan->setup.channel = params->channel; - if ((err = snd_pcm_channel_setup(pcm, &chan->setup))<0) + if (!chan->open) + return -EBADFD; + if (chan->mmap_control) + return -EBADFD; + if ((err = pcm->ops->channel_params(pcm, params)) < 0) return err; - return 0; + chan->valid_setup = 0; + setup.channel = params->channel; + return snd_pcm_channel_setup(pcm, &setup); } -int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) { - int fd; + int err; struct snd_pcm_chan *chan; - if (!pcm || !setup) - return -EINVAL; + return -EFAULT; if (setup->channel < 0 || setup->channel > 1) return -EINVAL; chan = &pcm->chan[setup->channel]; - fd = chan->fd; - if (fd < 0) - return -EINVAL; - if (chan->setup_is_valid) { + if (!chan->open) + return -EBADFD; + if (chan->valid_setup) { memcpy(setup, &chan->setup, sizeof(*setup)); return 0; } - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) - return -errno; + if ((err = pcm->ops->channel_setup(pcm, setup)) < 0) + return err; memcpy(&chan->setup, setup, sizeof(*setup)); - chan->setup_is_valid = 1; + chan->valid_setup = 1; return 0; } -int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * setup) +const snd_pcm_channel_setup_t* snd_pcm_channel_cached_setup(snd_pcm_t *pcm, int channel) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return 0; + if (channel < 0 || channel > 1) + return 0; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->valid_setup) + return 0; + return &chan->setup; +} + +int snd_pcm_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup) { - int fd; + struct snd_pcm_chan *chan; if (!pcm || !setup) - return -EINVAL; + return -EFAULT; if (channel < 0 || channel > 1) return -EINVAL; - fd = pcm->chan[channel].fd; - if (fd < 0) + chan = &pcm->chan[channel]; + if (!chan->open || !chan->valid_setup) + return -EBADFD; + if (chan->valid_voices_setup) { + if (setup->voice >= chan->setup.format.voices) + return -EINVAL; + memcpy(setup, &chan->voices_setup[setup->voice], sizeof(*setup)); + return 0; + } + return pcm->ops->voice_setup(pcm, channel, setup); +} + +const snd_pcm_voice_setup_t* snd_pcm_channel_cached_voice_setup(snd_pcm_t *pcm, int channel, unsigned int voice) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return 0; + if (channel < 0 || channel > 1) + return 0; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->valid_setup) + return 0; + if (voice >= chan->setup.format.voices) + return 0; + return &chan->voices_setup[voice]; +} + +int snd_pcm_all_voices_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup) +{ + struct snd_pcm_chan *chan; + snd_pcm_voice_setup_t *vs, *v; + unsigned int voice; + int err; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0) - return -errno; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->valid_setup) + return -EBADFD; + vs = calloc(chan->setup.format.voices, sizeof(*setup)); + for (voice = 0, v = vs; voice < chan->setup.format.voices; ++voice, ++v) { + v->voice = voice; + err = snd_pcm_voice_setup(pcm, channel, v); + if (err < 0) { + free(vs); + return err; + } + if (setup) { + memcpy(setup, v, sizeof(*setup)); + setup++; + } + } + chan->voices_setup = vs; + chan->valid_voices_setup = 1; return 0; } -int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status) +int snd_pcm_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) { - int fd; if (!pcm || !status) + return -EFAULT; + if (status->channel < 0 || status->channel > 1) return -EINVAL; - fd = pcm->chan[status->channel].fd; - if (fd < 0) - return -EINVAL; - if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0) - return -errno; - return 0; + if (!pcm->chan[status->channel].open) + return -EBADFD; + return pcm->ops->channel_status(pcm, status); } -int snd_pcm_playback_prepare(snd_pcm_t *pcm) +int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel) { + int err; if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0) - return -errno; + if (!pcm->chan[channel].open) + return -EBADFD; + err = pcm->ops->channel_prepare(pcm, channel); + if (err < 0) + return err; + snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PREPARED); return 0; } +int snd_pcm_playback_prepare(snd_pcm_t *pcm) +{ + return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_PLAYBACK); +} + int snd_pcm_capture_prepare(snd_pcm_t *pcm) { - if (!pcm) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0) - return -errno; - return 0; + return snd_pcm_channel_prepare(pcm, SND_PCM_CHANNEL_CAPTURE); } -int snd_pcm_channel_prepare(snd_pcm_t *pcm, int channel) +static int mmap_playback_go(snd_pcm_t *pcm, int channel) { - switch (channel) { - case SND_PCM_CHANNEL_PLAYBACK: - return snd_pcm_playback_prepare(pcm); - case SND_PCM_CHANNEL_CAPTURE: - return snd_pcm_capture_prepare(pcm); - default: + struct snd_pcm_chan *chan = &pcm->chan[channel]; + if (chan->mmap_control->status != SND_PCM_STATUS_PREPARED) + return -EBADFD; + if (chan->mmap_control->frag_data == 0) return -EIO; - } + chan->mmap_control->status = SND_PCM_STATUS_RUNNING; + pthread_mutex_lock(&chan->mutex); + pthread_cond_signal(&chan->status_cond); + pthread_cond_wait(&chan->ready_cond, &chan->mutex); + pthread_mutex_unlock(&chan->mutex); + return 0; } -int snd_pcm_playback_go(snd_pcm_t *pcm) +int snd_pcm_channel_go(snd_pcm_t *pcm, int channel) { + int err; + struct snd_pcm_chan *chan; if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0) - return -errno; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK && + chan->mmap_data_emulation) { + err = mmap_playback_go(pcm, channel); + if (err < 0) + return err; + } + err = pcm->ops->channel_go(pcm, channel); + if (err < 0) + return err; + if (channel == SND_PCM_CHANNEL_CAPTURE) + snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_RUNNING); return 0; } -int snd_pcm_capture_go(snd_pcm_t *pcm) +int snd_pcm_playback_go(snd_pcm_t *pcm) { - if (!pcm) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_GO) < 0) - return -errno; - return 0; + return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK); } -int snd_pcm_channel_go(snd_pcm_t *pcm, int channel) +int snd_pcm_capture_go(snd_pcm_t *pcm) { - switch (channel) { - case SND_PCM_CHANNEL_PLAYBACK: - return snd_pcm_playback_go(pcm); - case SND_PCM_CHANNEL_CAPTURE: - return snd_pcm_capture_go(pcm); - default: - return -EIO; - } + return snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_CAPTURE); } int snd_pcm_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync) { + int err; if (!pcm || !sync) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0) - return -errno; + return -EFAULT; + if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open && + !pcm->chan[SND_PCM_CHANNEL_CAPTURE].open) + return -EBADFD; + err = pcm->ops->sync_go(pcm, sync); + if (err < 0) + return err; + /* NYI: mmap emulation */ return 0; } -int snd_pcm_playback_drain(snd_pcm_t *pcm) +int snd_pcm_channel_drain(snd_pcm_t *pcm, int channel) { + int err; if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0) - return -errno; + if (!pcm->chan[channel].open) + return -EBADFD; + if (channel != SND_PCM_CHANNEL_PLAYBACK) + return -EBADFD; + err = pcm->ops->channel_drain(pcm, channel); + if (err < 0) + return err; + snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY); return 0; } -int snd_pcm_playback_flush(snd_pcm_t *pcm) +int snd_pcm_playback_drain(snd_pcm_t *pcm) { - if (!pcm) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0) - return -errno; - return 0; + return snd_pcm_channel_drain(pcm, SND_PCM_CHANNEL_PLAYBACK); } -int snd_pcm_capture_flush(snd_pcm_t *pcm) +int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel) { + int err; if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0) - return -errno; + if (!pcm->chan[channel].open) + return -EBADFD; + err = pcm->ops->channel_flush(pcm, channel); + if (err < 0) + return err; + snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_READY); return 0; } -int snd_pcm_channel_flush(snd_pcm_t *pcm, int channel) +int snd_pcm_playback_flush(snd_pcm_t *pcm) { - switch (channel) { - case SND_PCM_CHANNEL_PLAYBACK: - return snd_pcm_playback_flush(pcm); - case SND_PCM_CHANNEL_CAPTURE: - return snd_pcm_capture_flush(pcm); - default: - return -EIO; - } + return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_PLAYBACK); } -int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable) +int snd_pcm_capture_flush(snd_pcm_t *pcm) +{ + return snd_pcm_channel_flush(pcm, SND_PCM_CHANNEL_CAPTURE); +} + +int snd_pcm_channel_pause(snd_pcm_t *pcm, int channel, int enable) { + int err; if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - if (ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0) - return -errno; + if (!pcm->chan[channel].open) + return -EBADFD; + if (channel != SND_PCM_CHANNEL_PLAYBACK) + return -EBADFD; + err = pcm->ops->channel_pause(pcm, channel, enable); + if (err < 0) + return err; + snd_pcm_mmap_status_change(pcm, channel, SND_PCM_STATUS_PAUSED); return 0; } +int snd_pcm_playback_pause(snd_pcm_t *pcm, int enable) +{ + return snd_pcm_channel_pause(pcm, SND_PCM_CHANNEL_PLAYBACK, enable); +} + ssize_t snd_pcm_transfer_size(snd_pcm_t *pcm, int channel) { struct snd_pcm_chan *chan; - if (!pcm || channel < 0 || channel > 1) + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; chan = &pcm->chan[channel]; - if (!chan->setup_is_valid) + if (!chan->open) + return -EBADFD; + if (!chan->valid_setup) return -EBADFD; if (chan->setup.mode != SND_PCM_MODE_BLOCK) return -EBADFD; - return chan->setup.buf.block.frag_size; + return chan->setup.frag_size; } ssize_t snd_pcm_write(snd_pcm_t *pcm, const void *buffer, size_t size) { - ssize_t result; - - if (!pcm || (!buffer && size > 0) || size < 0) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; - result = write(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, buffer, size); - if (result < 0) - return -errno; - return result; + if (!pcm) + return -EFAULT; + if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open || + !pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup) + return -EBADFD; + if (size > 0 && !buffer) + return -EFAULT; + return pcm->ops->write(pcm, buffer, size); } -ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count) +ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) { - ssize_t result; - - if (!pcm || (!vector && count > 0) || count < 0) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd < 0) - return -EINVAL; -#if 0 - result = writev(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, vector, count); -#else - { - snd_v_args_t args; - args.vector = vector; - args.count = count; - result = ioctl(pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd, SND_IOCTL_WRITEV, &args); - } -#endif - if (result < 0) - return -errno; - return result; + if (!pcm) + return -EFAULT; + if (!pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open || + !pcm->chan[SND_PCM_CHANNEL_PLAYBACK].valid_setup) + return -EBADFD; + if (count > 0 && !vector) + return -EFAULT; + return pcm->ops->writev(pcm, vector, count); } ssize_t snd_pcm_read(snd_pcm_t *pcm, void *buffer, size_t size) { - ssize_t result; - - if (!pcm || (!buffer && size > 0) || size < 0) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0) - return -EINVAL; - result = read(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, buffer, size); - if (result < 0) - return -errno; - return result; + if (!pcm) + return -EFAULT; + if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open || + !pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup) + return -EBADFD; + if (size > 0 && !buffer) + return -EFAULT; + return pcm->ops->read(pcm, buffer, size); } -ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count) +ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) { - ssize_t result; + if (!pcm) + return -EFAULT; + if (!pcm->chan[SND_PCM_CHANNEL_CAPTURE].open || + !pcm->chan[SND_PCM_CHANNEL_CAPTURE].valid_setup) + return -EBADFD; + if (count > 0 && !vector) + return -EFAULT; + return pcm->ops->readv(pcm, vector, count); +} - if (!pcm || (!vector && count > 0) || count < 0) - return -EINVAL; - if (pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd < 0) +int snd_pcm_file_descriptor(snd_pcm_t* pcm, int channel) +{ + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; -#if 0 - result = readv(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, vector, count); -#else - { - snd_v_args_t args; - args.vector = vector; - args.count = count; - result = ioctl(pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd, SND_IOCTL_READV, &args); - } -#endif - if (result < 0) - return -errno; - return result; + if (!pcm->chan[channel].open) + return -EBADFD; + return pcm->ops->file_descriptor(pcm, channel); } -int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **buffer) +int snd_pcm_voices_mask(snd_pcm_t *pcm, int channel, bitset_t *client_vmask) { - snd_pcm_channel_info_t info; - int err, fd, prot; - void *caddr, *daddr; - struct snd_pcm_chan *chan; - - if (control) - *control = NULL; - if (buffer) - *buffer = NULL; - if (!pcm || channel < 0 || channel > 1 || !control || !buffer) - return -EINVAL; - chan = &pcm->chan[channel]; - fd = chan->fd; - if (fd < 0) + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; - memset(&info, 0, sizeof(info)); - info.channel = channel; - if ((err = snd_pcm_channel_info(pcm, &info))<0) - return err; - caddr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_CONTROL); - if (caddr == (caddr_t)-1 || caddr == NULL) - return -errno; - prot = channel == SND_PCM_CHANNEL_PLAYBACK ? PROT_WRITE : PROT_READ; - daddr = mmap(NULL, info.mmap_size, prot, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_DATA); - if (daddr == (caddr_t)-1 || daddr == NULL) { - err = -errno; - munmap(caddr, sizeof(snd_pcm_mmap_control_t)); - return err; - } - *control = chan->mmap_control = caddr; - *buffer = chan->mmap_data = daddr; - chan->mmap_size = info.mmap_size; - return 0; + if (!pcm->chan[channel].open) + return -EBADFD; + return pcm->ops->voices_mask(pcm, channel, client_vmask); } -int snd_pcm_munmap(snd_pcm_t *pcm, int channel) +int snd_pcm_bytes_per_second(snd_pcm_t *pcm, int channel) { struct snd_pcm_chan *chan; - if (!pcm || channel < 0 || channel > 1) + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) return -EINVAL; chan = &pcm->chan[channel]; - if (chan->mmap_control) { - munmap(chan->mmap_control, sizeof(snd_pcm_mmap_control_t)); - chan->mmap_control = NULL; - } - if (chan->mmap_data) { - munmap(chan->mmap_data, chan->mmap_size); - chan->mmap_data = NULL; - chan->mmap_size = 0; - } - return 0; + if (!chan->open || !chan->valid_setup) + return -EBADFD; + return snd_pcm_format_bytes_per_second(&chan->setup.format); } + diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c new file mode 100644 index 00000000..edcbe444 --- /dev/null +++ b/src/pcm/pcm_hw.c @@ -0,0 +1,471 @@ +/* + * PCM - Hardware + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcm_local.h" + +#define SND_FILE_PCM_PLAYBACK "/dev/snd/pcmC%iD%ip" +#define SND_FILE_PCM_CAPTURE "/dev/snd/pcmC%iD%ic" +#define SND_PCM_VERSION_MAX SND_PROTOCOL_VERSION(2, 0, 0) + +static int snd_pcm_hw_channel_close(snd_pcm_t *pcm, int channel) +{ + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + if (hw->chan[channel].fd >= 0) + if (close(hw->chan[channel].fd)) + return -errno; + return 0; +} + +int snd_pcm_hw_channel_fd(snd_pcm_t *pcm, int channel) +{ + snd_pcm_hw_t *hw; + if (!pcm) + return -EINVAL; + if (channel < 0 || channel > 1) + return -EINVAL; + hw = (snd_pcm_hw_t*) &pcm->private; + return hw->chan[channel].fd; +} + +static int snd_pcm_hw_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock) +{ + long flags; + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + + if ((flags = fcntl(fd, F_GETFL)) < 0) + return -errno; + if (nonblock) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +{ + int fd, channel; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + for (channel = 0; channel < 2; ++channel) { + fd = hw->chan[channel].fd; + if (fd >= 0) + break; + } + if (fd < 0) + return -EBADFD; + if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[info->channel].fd; + if (fd < 0) + return -EINVAL; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[params->channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[setup->channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t * setup) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_VOICE_SETUP, setup) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t * status) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[status->channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_STATUS, status) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_prepare(snd_pcm_t *pcm, int channel) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PREPARE) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_go(snd_pcm_t *pcm, int channel) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_GO) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + if (pcm->chan[SND_PCM_CHANNEL_PLAYBACK].open) + fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd; + else + fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd; + if (ioctl(fd, SND_PCM_IOCTL_SYNC_GO, sync) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_drain(snd_pcm_t *pcm, int channel) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_DRAIN) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_flush(snd_pcm_t *pcm, int channel) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_FLUSH) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_channel_pause(snd_pcm_t *pcm, int channel, int enable) +{ + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[channel].fd; + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PAUSE, &enable) < 0) + return -errno; + return 0; +} + +static ssize_t snd_pcm_hw_write(snd_pcm_t *pcm, const void *buffer, size_t size) +{ + ssize_t result; + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd; + result = write(fd, buffer, size); + if (result < 0) + return -errno; + return result; +} + +static ssize_t snd_pcm_hw_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) +{ + ssize_t result; + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd; +#if 0 + result = writev(fd, vector, count); +#else + { + snd_v_args_t args; + args.vector = vector; + args.count = count; + result = ioctl(fd, SND_IOCTL_WRITEV, &args); + } +#endif + if (result < 0) + return -errno; + return result; +} + +static ssize_t snd_pcm_hw_read(snd_pcm_t *pcm, void *buffer, size_t size) +{ + ssize_t result; + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd; + result = read(fd, buffer, size); + if (result < 0) + return -errno; + return result; +} + +ssize_t snd_pcm_hw_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) +{ + ssize_t result; + int fd; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + fd = hw->chan[SND_PCM_CHANNEL_CAPTURE].fd; +#if 0 + result = readv(fd, vector, count); +#else + { + snd_v_args_t args; + args.vector = vector; + args.count = count; + result = ioctl(fd, SND_IOCTL_READV, &args); + } +#endif + if (result < 0) + return -errno; + return result; +} + +static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize) +{ + void *caddr; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + caddr = mmap(NULL, csize, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, + hw->chan[channel].fd, SND_PCM_MMAP_OFFSET_CONTROL); + if (caddr == MAP_FAILED || caddr == NULL) + return -errno; + *control = caddr; + return 0; +} + +static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize) +{ + int prot; + void *daddr; + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + prot = channel == SND_PCM_CHANNEL_PLAYBACK ? PROT_WRITE : PROT_READ; + daddr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED, + hw->chan[channel].fd, SND_PCM_MMAP_OFFSET_DATA); + if (daddr == MAP_FAILED || daddr == NULL) + return -errno; + *buffer = daddr; + return 0; +} + +static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm UNUSED, int channel UNUSED, snd_pcm_mmap_control_t *control, size_t csize) +{ + if (munmap(control, csize) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm UNUSED, int channel UNUSED, void *buffer, size_t bsize) +{ + if (munmap(buffer, bsize) < 0) + return -errno; + return 0; +} + +static int snd_pcm_hw_file_descriptor(snd_pcm_t *pcm, int channel) +{ + snd_pcm_hw_t *hw = (snd_pcm_hw_t*) &pcm->private; + return hw->chan[channel].fd; +} + +static int snd_pcm_hw_voices_mask(snd_pcm_t *pcm UNUSED, int channel UNUSED, + bitset_t *client_vmask UNUSED) +{ + return 0; +} + +struct snd_pcm_ops snd_pcm_hw_ops = { + channel_close: snd_pcm_hw_channel_close, + channel_nonblock: snd_pcm_hw_channel_nonblock, + info: snd_pcm_hw_info, + channel_info: snd_pcm_hw_channel_info, + channel_params: snd_pcm_hw_channel_params, + channel_setup: snd_pcm_hw_channel_setup, + voice_setup: snd_pcm_hw_voice_setup, + channel_status: snd_pcm_hw_channel_status, + channel_prepare: snd_pcm_hw_channel_prepare, + channel_go: snd_pcm_hw_channel_go, + sync_go: snd_pcm_hw_sync_go, + channel_drain: snd_pcm_hw_channel_drain, + channel_flush: snd_pcm_hw_channel_flush, + channel_pause: snd_pcm_hw_channel_pause, + write: snd_pcm_hw_write, + writev: snd_pcm_hw_writev, + read: snd_pcm_hw_read, + readv: snd_pcm_hw_readv, + mmap_control: snd_pcm_hw_mmap_control, + mmap_data: snd_pcm_hw_mmap_data, + munmap_control: snd_pcm_hw_munmap_control, + munmap_data: snd_pcm_hw_munmap_data, + file_descriptor: snd_pcm_hw_file_descriptor, + voices_mask: snd_pcm_hw_voices_mask, +}; + +static int snd_pcm_hw_open_channel(int card, int device, int channel, int subdevice, int fmode, snd_ctl_t *ctl, int *ver) +{ + char filename[32]; + char *filefmt; + int err, fd; + int attempt = 0; + snd_pcm_channel_info_t info; + switch (channel) { + case SND_PCM_CHANNEL_PLAYBACK: + filefmt = SND_FILE_PCM_PLAYBACK; + break; + case SND_PCM_CHANNEL_CAPTURE: + filefmt = SND_FILE_PCM_CAPTURE; + break; + default: + return -EINVAL; + } + if ((err = snd_ctl_pcm_channel_prefer_subdevice(ctl, device, channel, subdevice)) < 0) + return err; + sprintf(filename, filefmt, card, device); + + __again: + if (attempt++ > 3) { + snd_ctl_close(ctl); + return -EBUSY; + } + if ((fd = open(filename, fmode)) < 0) { + err = -errno; + return err; + } + if (ioctl(fd, SND_PCM_IOCTL_PVERSION, ver) < 0) { + err = -errno; + close(fd); + return err; + } + if (SND_PROTOCOL_INCOMPATIBLE(*ver, SND_PCM_VERSION_MAX)) { + close(fd); + return -SND_ERROR_INCOMPATIBLE_VERSION; + } + if (subdevice >= 0) { + memset(&info, 0, sizeof(info)); + if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &info) < 0) { + err = -errno; + close(fd); + return err; + } + if (info.subdevice != subdevice) { + close(fd); + goto __again; + } + } + return fd; +} + +int snd_pcm_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode) +{ + int fmode, ver, err; + snd_pcm_t *pcm; + snd_pcm_hw_t *hw; + snd_ctl_t *ctl; + int pfd = -1, cfd = -1; + + if (!handle) + return -EFAULT; + *handle = NULL; + + if (card < 0 || card >= SND_CARDS) + return -EINVAL; + if ((err = snd_ctl_open(&ctl, card)) < 0) + return err; + if (mode & SND_PCM_OPEN_PLAYBACK) { + fmode = O_RDWR; + if (mode & SND_PCM_NONBLOCK_PLAYBACK) + fmode |= O_NONBLOCK; + pfd = snd_pcm_hw_open_channel(card, device, SND_PCM_CHANNEL_PLAYBACK, + subdevice, fmode, ctl, &ver); + if (pfd < 0) { + snd_ctl_close(ctl); + return pfd; + } + } + if (mode & SND_PCM_OPEN_CAPTURE) { + fmode = O_RDWR; + if (mode & SND_PCM_NONBLOCK_CAPTURE) + fmode |= O_NONBLOCK; + cfd = snd_pcm_hw_open_channel(card, device, SND_PCM_CHANNEL_CAPTURE, + subdevice, fmode, ctl, &ver); + if (cfd < 0) { + if (pfd >= 0) + close(pfd); + snd_ctl_close(ctl); + return cfd; + } + } + snd_ctl_close(ctl); + if (pfd < 0 && cfd < 0) + return -EINVAL; + + err = snd_pcm_abstract_open(handle, mode, SND_PCM_TYPE_HW, sizeof(snd_pcm_hw_t)); + if (err < 0) { + if (pfd >= 0) + close(pfd); + if (cfd >= 0) + close(cfd); + return err; + } + pcm = *handle; + pcm->ops = &snd_pcm_hw_ops; + hw = (snd_pcm_hw_t*) &pcm->private; + hw->card = card; + hw->device = device; + hw->ver = ver; + hw->chan[SND_PCM_CHANNEL_PLAYBACK].fd = pfd; + hw->chan[SND_PCM_CHANNEL_CAPTURE].fd = cfd; + return 0; +} + +int snd_pcm_open(snd_pcm_t **handle, int card, int device, int mode) +{ + return snd_pcm_open_subdevice(handle, card, device, -1, mode); +} + diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index eca8cedb..4bd07ae7 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -22,60 +22,133 @@ #include #include "asoundlib.h" -struct snd_pcm_plug { + +struct snd_pcm_ops { + int (*channel_close)(snd_pcm_t *pcm, int channel); + int (*channel_nonblock)(snd_pcm_t *pcm, int channel, int nonblock); + int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info); + int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); + int (*channel_params)(snd_pcm_t *pcm, snd_pcm_channel_params_t *params); + int (*channel_setup)(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup); + int (*voice_setup)(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup); + int (*channel_status)(snd_pcm_t *pcm, snd_pcm_channel_status_t *status); + int (*channel_prepare)(snd_pcm_t *pcm, int channel); + int (*channel_go)(snd_pcm_t *pcm, int channel); + int (*sync_go)(snd_pcm_t *pcm, snd_pcm_sync_t *sync); + int (*channel_drain)(snd_pcm_t *pcm, int channel); + int (*channel_flush)(snd_pcm_t *pcm, int channel); + int (*channel_pause)(snd_pcm_t *pcm, int channel, int enable); + ssize_t (*write)(snd_pcm_t *pcm, const void *buffer, size_t size); + ssize_t (*writev)(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); + ssize_t (*read)(snd_pcm_t *pcm, void *buffer, size_t size); + ssize_t (*readv)(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count); + int (*mmap_control)(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize); + int (*mmap_data)(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize); + int (*munmap_control)(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t *control, size_t csize); + int (*munmap_data)(snd_pcm_t *pcm, int channel, void *buffer, size_t bsize); + int (*file_descriptor)(snd_pcm_t* pcm, int channel); + int (*voices_mask)(snd_pcm_t *pcm, int channel, bitset_t *client_vmask); +}; + + +struct snd_pcm_plug_chan { snd_pcm_plugin_t *first; snd_pcm_plugin_t *last; void *alloc_ptr[2]; - long alloc_size[2]; + size_t alloc_size[2]; int alloc_lock[2]; - snd_pcm_mmap_control_t *mmap_control; - char *mmap_data; - long mmap_size; - pthread_t thread; - int thread_stop; - int setup_is_valid; - snd_pcm_channel_setup_t setup; - int hwstatus; }; -struct snd_pcm_chan { +typedef struct snd_pcm_plug { + int close_slave; + snd_pcm_t *slave; + struct snd_pcm_plug_chan chan[2]; +} snd_pcm_plug_t; + +struct snd_pcm_hw_chan { int fd; - int setup_is_valid; +}; + +typedef struct snd_pcm_hw { + int card; + int device; + int ver; + struct snd_pcm_hw_chan chan[2]; +} snd_pcm_hw_t; + +struct snd_pcm_chan { + int open; + int mode; + int valid_setup; snd_pcm_channel_setup_t setup; + int valid_voices_setup; + snd_pcm_voice_setup_t *voices_setup; snd_pcm_mmap_control_t *mmap_control; + size_t mmap_control_size; + int mmap_control_emulation; char *mmap_data; - long mmap_size; - struct snd_pcm_plug plug; + size_t mmap_data_size; + int mmap_data_emulation; + pthread_t mmap_thread; + int mmap_thread_stop; + pthread_mutex_t mutex; + pthread_cond_t status_cond; + pthread_cond_t ready_cond; }; struct snd_pcm { - int card; - int device; + snd_pcm_type_t type; int mode; - int ver; + struct snd_pcm_ops *ops; struct snd_pcm_chan chan[2]; + int private[0]; }; -unsigned int snd_pcm_plugin_formats(unsigned int formats); -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_info_t *hwinfo); +void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus); + +int snd_pcm_abstract_open(snd_pcm_t **handle, int mode, snd_pcm_type_t type, size_t extra); + + +unsigned int snd_pcm_plug_formats(unsigned int formats); +int snd_pcm_plug_slave_params(snd_pcm_channel_params_t *params, + snd_pcm_channel_info_t *slave_info, + snd_pcm_channel_params_t *slave_params); +int snd_pcm_plug_format(snd_pcm_plugin_handle_t *pcm, + snd_pcm_channel_params_t *params, + snd_pcm_channel_params_t *slave_params); + +ssize_t snd_pcm_plug_write_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *src_voices, size_t size); +ssize_t snd_pcm_plug_read_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *dst_voices_final, size_t size); +ssize_t snd_pcm_plug_client_voices_iovec(snd_pcm_plugin_handle_t *handle, int channel, + const struct iovec *vector, unsigned long count, + snd_pcm_plugin_voice_t **voices); +ssize_t snd_pcm_plug_client_voices_buf(snd_pcm_plugin_handle_t *handle, int channel, + char *buf, size_t count, + snd_pcm_plugin_voice_t **voices); + +int snd_pcm_plug_playback_voices_mask(snd_pcm_plugin_handle_t *handle, + bitset_t *client_vmask); +int snd_pcm_plug_capture_voices_mask(snd_pcm_plugin_handle_t *handle, + bitset_t *client_vmask); +int snd_pcm_plugin_client_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices); + +void *snd_pcm_plug_buf_alloc(snd_pcm_t *pcm, int channel, size_t size); +void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int channel, void *ptr); #define ROUTE_PLUGIN_RESOLUTION 16 int getput_index(int format); -int copy_index(int src_format, int dst_format); +int copy_index(int format); +int conv_index(int src_format, int dst_format); -void zero_voice(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_voice_t *dst_voice, - size_t samples); +void snd_pcm_plugin_silence_voice(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *dst_voice, + size_t samples); #ifdef PLUGIN_DEBUG -#define pdprintf( args... ) printf( "plugin: " ##args) +#define pdprintf( args... ) fprintf(stderr, "plugin: " ##args) #else #define pdprintf( args... ) { ; } #endif diff --git a/src/pcm/pcm_loopback.c b/src/pcm/pcm_loopback.c deleted file mode 100644 index 9b07c69b..00000000 --- a/src/pcm/pcm_loopback.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * PCM LoopBack Interface - main file - * Copyright (c) 1998 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "asoundlib.h" - -#define SND_FILE_PCM_LB "/proc/asound/%i/pcmloopD%iS%i%s" -#define SND_PCM_LB_VERSION_MAX SND_PROTOCOL_VERSION(1, 0, 0) - -struct snd_pcm_loopback { - int card; - int device; - int fd; - long mode; - size_t buffer_size; - char *buffer; -}; - -int snd_pcm_loopback_open(snd_pcm_loopback_t **handle, int card, int device, int subchn, int mode) -{ - int fd, ver; - char filename[32]; - snd_pcm_loopback_t *lb; - - *handle = NULL; - - if (card < 0 || card >= SND_CARDS) - return -EINVAL; - sprintf(filename, SND_FILE_PCM_LB, card, device, subchn, - mode == SND_PCM_LB_OPEN_CAPTURE ? "c" : "p"); - if ((fd = open(filename, mode)) < 0) { - snd_card_load(card); - if ((fd = open(filename, mode)) < 0) - return -errno; - } - if (ioctl(fd, SND_PCM_LB_IOCTL_PVERSION, &ver) < 0) { - close(fd); - return -errno; - } - if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_PCM_LB_VERSION_MAX)) { - close(fd); - return -SND_ERROR_INCOMPATIBLE_VERSION; - } - lb = (snd_pcm_loopback_t *) calloc(1, sizeof(snd_pcm_loopback_t)); - if (lb == NULL) { - close(fd); - return -ENOMEM; - } - lb->card = card; - lb->device = device; - lb->fd = fd; - lb->mode = SND_PCM_LB_STREAM_MODE_RAW; - *handle = lb; - return 0; -} - -int snd_pcm_loopback_close(snd_pcm_loopback_t *lb) -{ - int res; - - if (!lb) - return -EINVAL; - if (lb->buffer) - free(lb->buffer); - res = close(lb->fd) < 0 ? -errno : 0; - free(lb); - return res; -} - -int snd_pcm_loopback_file_descriptor(snd_pcm_loopback_t *lb) -{ - if (!lb) - return -EINVAL; - return lb->fd; -} - -int snd_pcm_loopback_block_mode(snd_pcm_loopback_t *lb, int enable) -{ - long flags; - - if (!lb) - return -EINVAL; - if ((flags = fcntl(lb->fd, F_GETFL)) < 0) - return -errno; - if (enable) - flags &= ~O_NONBLOCK; - else - flags |= O_NONBLOCK; - if (fcntl(lb->fd, F_SETFL, flags) < 0) - return -errno; - return 0; -} - -int snd_pcm_loopback_stream_mode(snd_pcm_loopback_t *lb, int mode) -{ - if (!lb || (mode != SND_PCM_LB_STREAM_MODE_RAW && - mode != SND_PCM_LB_STREAM_MODE_PACKET)) - return -EINVAL; - lb->mode = mode; - if (ioctl(lb->fd, SND_PCM_LB_IOCTL_STREAM_MODE, &lb->mode) < 0) - return -errno; - return 0; -} - -int snd_pcm_loopback_format(snd_pcm_loopback_t *lb, snd_pcm_format_t * format) -{ - if (!lb) - return -EINVAL; - if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, format) < 0) - return -errno; - return 0; -} - -int snd_pcm_loopback_status(snd_pcm_loopback_t *lb, snd_pcm_loopback_status_t * status) -{ - if (!lb) - return -EINVAL; - if (ioctl(lb->fd, SND_PCM_LB_IOCTL_FORMAT, status) < 0) - return -errno; - return 0; -} - -ssize_t snd_pcm_loopback_read(snd_pcm_loopback_t *lb, snd_pcm_loopback_callbacks_t *callbacks) -{ - ssize_t result = 0, res, count; - size_t size; - char *buf; - snd_pcm_loopback_header_t header; - - if (!lb || !callbacks) - return -EINVAL; - if (callbacks->max_buffer_size == 0) - size = 64 * 1024; - else - size = callbacks->max_buffer_size < 16 ? 16 : callbacks->max_buffer_size; - while (1) { - if (lb->mode == SND_PCM_LB_STREAM_MODE_RAW) { - header.size = size; - header.type = SND_PCM_LB_TYPE_DATA; - } else { - res = read(lb->fd, &header, sizeof(header)); - if (res < 0) - return -errno; - if (res == 0) - break; - if (res != sizeof(header)) - return -EBADFD; - result += res; - } - switch (header.type) { - case SND_PCM_LB_TYPE_DATA: - if (lb->buffer_size < size) { - buf = (char *) realloc(lb->buffer, size); - if (buf == NULL) - return -ENOMEM; - lb->buffer = buf; - lb->buffer_size = size; - } else { - buf = lb->buffer; - } - while (header.size > 0) { - count = header.size; - if (count > size) - count = size; - res = read(lb->fd, buf, count); - if (res < 0) - return -errno; - result += res; - if (lb->mode == SND_PCM_LB_STREAM_MODE_PACKET && res != count) - return -EBADFD; - if (res == 0) - break; - if (callbacks->data) - callbacks->data(callbacks->private_data, buf, res); - if (res < count && lb->mode == SND_PCM_LB_STREAM_MODE_RAW) - break; - header.size -= res; - } - break; - case SND_PCM_LB_TYPE_FORMAT: - { - snd_pcm_format_t format; - - res = read(lb->fd, &format, sizeof(format)); - if (res < 0) - return -errno; - result += res; - if (res != sizeof(format)) - return -EBADFD; - if (callbacks->format_change) - callbacks->format_change(callbacks->private_data, &format); - } - break; - case SND_PCM_LB_TYPE_POSITION: - { - unsigned int pos; - - res = read(lb->fd, &pos, sizeof(pos)); - if (res < 0) - return -errno; - result += res; - if (res != sizeof(pos)) - return -EBADFD; - if (callbacks->position_change) - callbacks->position_change(callbacks->private_data, pos); - } - break; - case SND_PCM_LB_TYPE_SILENCE: - if (callbacks->silence) - callbacks->silence(callbacks->private_data, header.size); - break; - } - } - return result; -} diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index 707f24a3..bd43d22d 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -21,11 +21,14 @@ #ifdef __KERNEL__ #include "../include/driver.h" +#include "../include/pcm.h" +#include "../include/pcm_plugin.h" #else #include #include #include #include +#include #include #include #include @@ -187,10 +190,6 @@ int snd_pcm_format_physical_width(int format) ssize_t snd_pcm_format_size(int format, size_t samples) { - if (samples < 0) - return -EINVAL; - if (samples == 0) - return samples; switch (format) { case SND_PCM_SFMT_S8: case SND_PCM_SFMT_U8: @@ -229,6 +228,11 @@ ssize_t snd_pcm_format_size(int format, size_t samples) } } +ssize_t snd_pcm_format_bytes_per_second(snd_pcm_format_t *format) +{ + return snd_pcm_format_size(format->format, format->voices * format->rate); +} + const char *snd_pcm_get_format_name(int format) { static char *formats[] = { @@ -265,23 +269,9 @@ const char *snd_pcm_get_format_name(int format) return formats[format]; } -unsigned char snd_pcm_format_silence(int format) +u_int64_t snd_pcm_format_silence_64(int format) { switch (format) { - case SND_PCM_SFMT_IMA_ADPCM: /* special case */ - case SND_PCM_SFMT_MPEG: - case SND_PCM_SFMT_GSM: - case SND_PCM_SFMT_MU_LAW: - case SND_PCM_SFMT_A_LAW: - return 0; - case SND_PCM_SFMT_U8: - case SND_PCM_SFMT_U16_LE: - case SND_PCM_SFMT_U16_BE: - case SND_PCM_SFMT_U24_LE: - case SND_PCM_SFMT_U24_BE: - case SND_PCM_SFMT_U32_LE: - case SND_PCM_SFMT_U32_BE: - return 0x80; case SND_PCM_SFMT_S8: case SND_PCM_SFMT_S16_LE: case SND_PCM_SFMT_S16_BE: @@ -290,15 +280,135 @@ unsigned char snd_pcm_format_silence(int format) case SND_PCM_SFMT_S32_LE: case SND_PCM_SFMT_S32_BE: return 0; - case SND_PCM_SFMT_FLOAT: - case SND_PCM_SFMT_FLOAT64: + case SND_PCM_SFMT_U8: + return 0x8080808080808080UL; + case SND_PCM_SFMT_U16_LE: + case SND_PCM_SFMT_U24_LE: + case SND_PCM_SFMT_U32_LE: +#if defined(LITTLE_ENDIAN) + return 0x8000800080008000UL; +#elif defined(BIG_ENDIAN) + return 0x0080008000800080UL; +#else +#error "endian" +#endif + case SND_PCM_SFMT_U16_BE: + case SND_PCM_SFMT_U24_BE: + case SND_PCM_SFMT_U32_BE: +#if defined(LITTLE_ENDIAN) + return 0x0000008000000080UL; +#elif defined(BIG_ENDIAN) + return 0x8000000080000000UL; +#else +#error "endian" +#endif + case SND_PCM_SFMT_FLOAT_LE: +#if defined(LITTLE_ENDIAN) + return (float)0.0; +#elif defined(BIG_ENDIAN) + return bswap_32((u_int32_t)((float)0.0)); +#else +#error "endian" +#endif + case SND_PCM_SFMT_FLOAT64_LE: +#if defined(LITTLE_ENDIAN) + return (double)0.0; +#elif defined(BIG_ENDIAN) + return bswap_64((u_int64_t)((double)0.0)); +#else +#error "endian" +#endif + case SND_PCM_SFMT_FLOAT_BE: +#if defined(LITTLE_ENDIAN) + return bswap_32((u_int32_t)((float)0.0)); +#elif defined(BIG_ENDIAN) + return (float)0.0; +#else +#error "endian" +#endif + case SND_PCM_SFMT_FLOAT64_BE: +#if defined(LITTLE_ENDIAN) + return bswap_64((u_int64_t)((double)0.0)); +#elif defined(BIG_ENDIAN) + return (double)0.0; +#else +#error "endian" +#endif case SND_PCM_SFMT_IEC958_SUBFRAME_LE: case SND_PCM_SFMT_IEC958_SUBFRAME_BE: return 0; + case SND_PCM_SFMT_MU_LAW: + return 0x7f7f7f7f7f7f7f7fUL; + case SND_PCM_SFMT_A_LAW: + return 0x5555555555555555UL; + case SND_PCM_SFMT_IMA_ADPCM: /* special case */ + case SND_PCM_SFMT_MPEG: + case SND_PCM_SFMT_GSM: + return 0; } return 0; } +u_int32_t snd_pcm_format_silence_32(int format) +{ + return (u_int32_t)snd_pcm_format_silence_64(format); +} + +u_int16_t snd_pcm_format_silence_16(int format) +{ + return (u_int16_t)snd_pcm_format_silence_64(format); +} + +u_int8_t snd_pcm_format_silence(int format) +{ + return (u_int8_t)snd_pcm_format_silence_64(format); +} + +ssize_t snd_pcm_format_set_silence(int format, void *data, size_t count) +{ + size_t count1; + + if (count == 0) + return 0; + switch (snd_pcm_format_width(format)) { + case 4: + case 8: { + u_int8_t silence = snd_pcm_format_silence_64(format); + memset(data, silence, count); + break; + } + case 16: { + u_int16_t silence = snd_pcm_format_silence_64(format); + if (count % 2) + return -EINVAL; + count1 = count / 2; + while (count1-- > 0) + *((u_int16_t *)data)++ = silence; + break; + } + case 32: { + u_int32_t silence = snd_pcm_format_silence_64(format); + if (count % 4) + return -EINVAL; + count1 = count / 4; + while (count1-- > 0) + *((u_int32_t *)data)++ = silence; + break; + } + case 64: { + u_int64_t silence = snd_pcm_format_silence_64(format); + if (count % 8) + return -EINVAL; + count1 = count / 8; + while (count1-- > 0) + *((u_int64_t *)data)++ = silence; + } + default: + return -EINVAL; + } + return count; +} + static int linear_formats[4*2*2] = { SND_PCM_SFMT_S8, SND_PCM_SFMT_U8, diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c new file mode 100644 index 00000000..5db9fb81 --- /dev/null +++ b/src/pcm/pcm_mmap.c @@ -0,0 +1,1139 @@ +/* + * PCM Interface - mmap + * Copyright (c) 2000 by Abramo Bagnara + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include "pcm_local.h" + +static void snd_pcm_mmap_clear(snd_pcm_t *pcm, int channel) +{ + struct snd_pcm_chan *chan = &pcm->chan[channel]; + chan->mmap_control->frag_io = 0; + chan->mmap_control->frag_data = 0; + chan->mmap_control->pos_io = 0; + chan->mmap_control->pos_data = 0; +} + +void snd_pcm_mmap_status_change(snd_pcm_t *pcm, int channel, int newstatus) +{ + struct snd_pcm_chan *chan = &pcm->chan[channel]; + + if (!chan->mmap_control_emulation) + return; + if (newstatus < 0) { + snd_pcm_channel_status_t status; + status.channel = channel; + if (snd_pcm_channel_status(pcm, &status) < 0) + newstatus = SND_PCM_STATUS_NOTREADY; + else + newstatus = status.status; + } + if (chan->mmap_control->status != newstatus) { + if (newstatus == SND_PCM_STATUS_READY || + (newstatus == SND_PCM_STATUS_PREPARED && + chan->mmap_control->status != SND_PCM_STATUS_READY)) + snd_pcm_mmap_clear(pcm, channel); + chan->mmap_control->status = newstatus; + pthread_mutex_lock(&chan->mutex); + pthread_cond_signal(&chan->status_cond); + pthread_mutex_unlock(&chan->mutex); + } +} + +static ssize_t snd_pcm_mmap_playback_frags_used(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + ssize_t frags_used; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + frags_used = chan->mmap_control->frag_data - chan->mmap_control->frag_io; + if (frags_used < (ssize_t)(chan->setup.frags - chan->setup.frag_boundary)) + frags_used += chan->setup.frag_boundary; + return frags_used; +} + +static size_t snd_pcm_mmap_capture_frags_used(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + ssize_t frags_used; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + frags_used = chan->mmap_control->frag_io - chan->mmap_control->frag_data; + if (frags_used < 0) + frags_used += chan->setup.frag_boundary; + return frags_used; +} + +static size_t snd_pcm_mmap_playback_frags_free(snd_pcm_t *pcm) +{ + return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.frags - snd_pcm_mmap_playback_frags_used(pcm); +} + +static ssize_t snd_pcm_mmap_capture_frags_free(snd_pcm_t *pcm) +{ + return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.frags - snd_pcm_mmap_capture_frags_used(pcm); +} + +int snd_pcm_mmap_frags_used(snd_pcm_t *pcm, int channel, ssize_t *frags) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + *frags = snd_pcm_mmap_playback_frags_used(pcm); + else + *frags = snd_pcm_mmap_capture_frags_used(pcm); + return 0; +} + +int snd_pcm_mmap_frags_free(snd_pcm_t *pcm, int channel, ssize_t *frags) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + *frags = snd_pcm_mmap_playback_frags_free(pcm); + else + *frags = snd_pcm_mmap_capture_frags_free(pcm); + return 0; +} + +static ssize_t snd_pcm_mmap_playback_bytes_used(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + ssize_t bytes_used; + // snd_pcm_update_pos(pcm, SND_PCM_CHANNEL_PLAYBACK); + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + bytes_used = chan->mmap_control->pos_data - chan->mmap_control->pos_io; + if (bytes_used < (ssize_t)(chan->setup.buffer_size - chan->setup.pos_boundary)) + bytes_used += chan->setup.pos_boundary; + return bytes_used; +} + +static size_t snd_pcm_mmap_capture_bytes_used(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + ssize_t bytes_used; + // snd_pcm_update_pos(pcm, SND_PCM_CHANNEL_CAPTURE); + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + bytes_used = chan->mmap_control->pos_io - chan->mmap_control->pos_data; + if (bytes_used < 0) + bytes_used += chan->setup.pos_boundary; + return bytes_used; +} + +int snd_pcm_mmap_bytes_used(snd_pcm_t *pcm, int channel, ssize_t *frags) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + *frags = snd_pcm_mmap_playback_bytes_used(pcm); + else + *frags = snd_pcm_mmap_capture_bytes_used(pcm); + return 0; +} + +static size_t snd_pcm_mmap_playback_bytes_free(snd_pcm_t *pcm) +{ + return pcm->chan[SND_PCM_CHANNEL_PLAYBACK].setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm); +} + +static ssize_t snd_pcm_mmap_capture_bytes_free(snd_pcm_t *pcm) +{ + return pcm->chan[SND_PCM_CHANNEL_CAPTURE].setup.buffer_size - snd_pcm_mmap_capture_bytes_used(pcm); +} + +int snd_pcm_mmap_bytes_free(snd_pcm_t *pcm, int channel, ssize_t *frags) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + *frags = snd_pcm_mmap_playback_bytes_free(pcm); + else + *frags = snd_pcm_mmap_capture_bytes_free(pcm); + return 0; +} + +static int snd_pcm_mmap_playback_ready(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (chan->mmap_control->status == SND_PCM_STATUS_XRUN) + return -EPIPE; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + return (chan->setup.frags - snd_pcm_mmap_playback_frags_used(pcm)) >= chan->setup.buf.block.frags_min; + } else { + return (chan->setup.buffer_size - snd_pcm_mmap_playback_bytes_used(pcm)) >= chan->setup.buf.stream.bytes_min; + } +} + +static int snd_pcm_mmap_capture_ready(snd_pcm_t *pcm) +{ + struct snd_pcm_chan *chan; + int ret = 0; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (chan->mmap_control->status == SND_PCM_STATUS_XRUN) { + ret = -EPIPE; + if (chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) + return -EPIPE; + } + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + if (snd_pcm_mmap_capture_frags_used(pcm) >= chan->setup.buf.block.frags_min) + return 1; + } else { + if (snd_pcm_mmap_capture_bytes_used(pcm) >= chan->setup.buf.stream.bytes_min) + return 1; + } + return ret; +} + +int snd_pcm_mmap_ready(snd_pcm_t *pcm, int channel) +{ + struct snd_pcm_chan *chan; + snd_pcm_mmap_control_t *ctrl; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open || !chan->mmap_control) + return -EBADFD; + ctrl = chan->mmap_control; + if (ctrl->status < SND_PCM_STATUS_PREPARED) + return -EBADFD; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + return snd_pcm_mmap_playback_ready(pcm); + } else { + return snd_pcm_mmap_capture_ready(pcm); + } +} + +/* Bytes transferrable */ +static size_t snd_pcm_mmap_bytes_playback(snd_pcm_t *pcm, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + snd_pcm_mmap_control_t *ctrl = chan->mmap_control; + size_t bytes_cont, bytes_free; + unsigned int pos_data = ctrl->pos_data; + unsigned int pos_io = ctrl->pos_io; + int bytes_used = pos_data - pos_io; + if (bytes_used < -(int)(chan->setup.buf.stream.bytes_xrun_max + chan->setup.frag_size)) + bytes_used += chan->setup.pos_boundary; + bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size); + if (bytes_cont < size) + size = bytes_cont; + bytes_free = chan->setup.buffer_size - bytes_used; + if (bytes_free < size) + size = (bytes_free / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align; + return size; +} + +/* Bytes transferrable */ +static size_t snd_pcm_mmap_bytes_capture(snd_pcm_t *pcm, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + snd_pcm_mmap_control_t *ctrl = chan->mmap_control; + size_t bytes_cont; + unsigned int pos_data = ctrl->pos_data; + unsigned int pos_io = ctrl->pos_io; + int bytes_used = pos_io - pos_data; + if (bytes_used < 0) + bytes_used += chan->setup.pos_boundary; + bytes_cont = chan->setup.buffer_size - (pos_data % chan->setup.buffer_size); + if (bytes_cont < size) + size = bytes_cont; + if ((size_t) bytes_used < size) + size = (bytes_used / chan->setup.buf.stream.bytes_align) * chan->setup.buf.stream.bytes_align; + return size; +} + +typedef int (*transfer_f)(snd_pcm_t *pcm, size_t hwoff, void *data, size_t off, size_t size); + + +static ssize_t snd_pcm_mmap_write1(snd_pcm_t *pcm, const void *data, size_t count, transfer_f transfer) +{ + struct snd_pcm_chan *chan; + snd_pcm_mmap_control_t *ctrl; + size_t frag_size; + size_t offset = 0; + size_t result = 0; + int err; + + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + ctrl = chan->mmap_control; + if (ctrl->status < SND_PCM_STATUS_PREPARED) + return -EBADFD; + frag_size = chan->setup.frag_size; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + if (count % frag_size != 0) + return -EINVAL; + } else { + int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); + if (tmp > 0) { + int r = count % tmp; + if (r > 0) { + count -= r; + if (count == 0) + return -EINVAL; + } + } + } + while (count > 0) { + size_t bytes; + int ready = snd_pcm_mmap_playback_ready(pcm); + if (ready < 0) + return ready; + if (!ready) { + struct pollfd pfd; + if (ctrl->status != SND_PCM_STATUS_RUNNING) + return result > 0 ? result : -EPIPE; + if (chan->mode & SND_PCM_NONBLOCK) + return result > 0 ? result : -EAGAIN; + pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_PLAYBACK); + pfd.events = POLLOUT | POLLERR; + ready = poll(&pfd, 1, 10000); + if (ready < 0) + return result > 0 ? result : ready; + if (ready && pfd.revents & POLLERR) + return result > 0 ? result : -EPIPE; + assert(snd_pcm_mmap_playback_ready(pcm)); + } + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frag_data, frag; + frag_data = ctrl->frag_data; + frag = frag_data % chan->setup.frags; + err = transfer(pcm, frag_size * frag, (void *) data, offset, frag_size); + if (err < 0) + return result > 0 ? result : err; + if (ctrl->status == SND_PCM_STATUS_XRUN) + return result > 0 ? result : -EPIPE; + frag_data++; + if (frag_data == chan->setup.frag_boundary) { + ctrl->frag_data = 0; + ctrl->pos_data = 0; + } else { + ctrl->frag_data = frag_data; + ctrl->pos_data += frag_size; + } + bytes = frag_size; + } else { + size_t pos_data; + bytes = snd_pcm_mmap_bytes_playback(pcm, count); + pos_data = ctrl->pos_data; + err = transfer(pcm, pos_data % chan->setup.buffer_size, (void *) data, offset, bytes); + if (err < 0) + return result > 0 ? result : err; + if (ctrl->status == SND_PCM_STATUS_XRUN) + return result > 0 ? result : -EPIPE; + pos_data += bytes; + if (pos_data == chan->setup.pos_boundary) { + ctrl->pos_data = 0; + ctrl->frag_data = 0; + } else { + ctrl->pos_data = pos_data; + ctrl->frag_data = pos_data / chan->setup.frags; + } + } + offset += bytes; + count -= bytes; + result += bytes; + } + + if (ctrl->status == SND_PCM_STATUS_PREPARED && + (chan->setup.start_mode == SND_PCM_START_DATA || + (chan->setup.start_mode == SND_PCM_START_FULL && + !snd_pcm_mmap_playback_ready(pcm)))) { + err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_PLAYBACK); + if (err < 0) + return result > 0 ? result : err; + } + return result; +} + +static int transfer_write(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + const char *buf = data; + unsigned int v, voices; +#define COPY_LABELS +#include "plugin/plugin_ops.h" +#undef COPY_LABELS + void *copy; + snd_pcm_voice_setup_t *vsetup; + int idx; + size_t vsize, ssize; + idx = copy_index(chan->setup.format.format); + if (idx < 0) + return idx; + copy = copy_labels[idx]; + voices = chan->setup.format.voices; + vsetup = chan->voices_setup; + vsize = snd_pcm_format_size(chan->setup.format.format, 1); + ssize = vsize * chan->setup.format.voices; + hwoff /= ssize; + size /= ssize; + for (v = 0; v < voices; ++v, ++vsetup) { + const char *src; + char *dst; + size_t dst_step; + size_t samples = size; + if (vsetup->first % 8 != 0 || + vsetup->step % 8 != 0) + return -EINVAL; + src = buf + off + v * vsize; + dst_step = vsetup->step / 8; + dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; + while (samples-- > 0) { + goto *copy; +#define COPY_END after +#include "plugin/plugin_ops.h" +#undef COPY_END + after: + src += ssize; + dst += dst_step; + } + } + return 0; +} + +ssize_t snd_pcm_mmap_write(snd_pcm_t *pcm, const void *buffer, size_t count) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (count > 0 && !buffer) + return -EFAULT; + if (!chan->setup.format.interleave && chan->setup.format.voices > 1) + return -EINVAL; + return snd_pcm_mmap_write1(pcm, buffer, count, transfer_write); +} + +static int transfer_writev(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + struct iovec *vec = data; + unsigned int v, voices; +#define COPY_LABELS +#include "plugin/plugin_ops.h" +#undef COPY_LABELS + void *copy; + snd_pcm_voice_setup_t *vsetup; + int idx; + size_t ssize; + idx = copy_index(chan->setup.format.format); + if (idx < 0) + return idx; + copy = copy_labels[idx]; + voices = chan->setup.format.voices; + vsetup = chan->voices_setup; + ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); + hwoff /= ssize; + size /= ssize; + off /= voices; + for (v = 0; v < voices; ++v, ++vsetup, ++vec) { + const char *src; + char *dst; + size_t dst_step; + size_t samples = size; + if (vsetup->first % 8 != 0 || + vsetup->step % 8 != 0) + return -EINVAL; + src = vec->iov_base + off; + dst_step = vsetup->step / 8; + dst = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; + while (samples-- > 0) { + goto *copy; +#define COPY_END after +#include "plugin/plugin_ops.h" +#undef COPY_END + after: + src += ssize; + dst += dst_step; + } + } + return 0; +} + +ssize_t snd_pcm_mmap_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount) +{ + struct snd_pcm_chan *chan; + size_t result = 0; + unsigned int b; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (vcount > 0 && !vector) + return -EFAULT; + if (chan->setup.format.interleave) { + for (b = 0; b < vcount; b++) { + int ret; + ret = snd_pcm_mmap_write1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write); + if (ret < 0) + return result > 0 ? result : ret; + result += ret; + } + } else { + unsigned int voices = chan->setup.format.voices; + unsigned long bcount; + if (vcount % voices) + return -EINVAL; + bcount = vcount / voices; + for (b = 0; b < bcount; b++) { + unsigned int v; + int ret; + size_t count = 0; + count = vector[0].iov_len; + for (v = 0; v < voices; ++v) { + if (vector[v].iov_len != count) + return -EINVAL; + } + ret = snd_pcm_mmap_write1(pcm, vector, count * voices, transfer_writev); + if (ret < 0) + return result > 0 ? result : ret; + result += ret; + if ((size_t)ret != count * voices) + break; + vector += voices; + } + } + return result; +} + +static ssize_t snd_pcm_mmap_read1(snd_pcm_t *pcm, void *data, size_t count, transfer_f transfer) +{ + struct snd_pcm_chan *chan; + snd_pcm_mmap_control_t *ctrl; + size_t frag_size; + size_t offset = 0; + size_t result = 0; + int err; + + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + ctrl = chan->mmap_control; + if (ctrl->status < SND_PCM_STATUS_PREPARED) + return -EBADFD; + frag_size = chan->setup.frag_size; + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + if (count % frag_size != 0) + return -EINVAL; + } else { + int tmp = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); + if (tmp > 0) { + int r = count % tmp; + if (r > 0) { + count -= r; + if (count == 0) + return -EINVAL; + } + } + } + if (ctrl->status == SND_PCM_STATUS_PREPARED && + chan->setup.start_mode == SND_PCM_START_DATA) { + err = snd_pcm_channel_go(pcm, SND_PCM_CHANNEL_CAPTURE); + if (err < 0) + return err; + } + while (count > 0) { + size_t bytes; + int ready = snd_pcm_mmap_capture_ready(pcm); + if (ready < 0) + return ready; + if (!ready) { + struct pollfd pfd; + if (ctrl->status != SND_PCM_STATUS_RUNNING) + return result > 0 ? result : -EPIPE; + if (chan->mode & SND_PCM_NONBLOCK) + return result > 0 ? result : -EAGAIN; + pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_CAPTURE); + pfd.events = POLLIN | POLLERR; + ready = poll(&pfd, 1, 10000); + if (ready < 0) + return result > 0 ? result : ready; + if (ready && pfd.revents & POLLERR) + return result > 0 ? result : -EPIPE; + assert(snd_pcm_mmap_capture_ready(pcm)); + } + if (chan->setup.mode == SND_PCM_MODE_BLOCK) { + size_t frag_data, frag; + frag_data = ctrl->frag_data; + frag = frag_data % chan->setup.frags; + err = transfer(pcm, frag_size * frag, data, offset, frag_size); + if (err < 0) + return result > 0 ? result : err; + if (ctrl->status == SND_PCM_STATUS_XRUN && + chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) + return result > 0 ? result : -EPIPE; + frag_data++; + if (frag_data == chan->setup.frag_boundary) { + ctrl->frag_data = 0; + ctrl->pos_data = 0; + } else { + ctrl->frag_data = frag_data; + ctrl->pos_data += frag_size; + } + bytes = frag_size; + } else { + size_t pos_data; + bytes = snd_pcm_mmap_bytes_capture(pcm, count); + pos_data = ctrl->pos_data; + err = transfer(pcm, pos_data % chan->setup.buffer_size, data, offset, bytes); + if (err < 0) + return result > 0 ? result : err; + if (ctrl->status == SND_PCM_STATUS_XRUN && + chan->setup.xrun_mode == SND_PCM_XRUN_DRAIN) + return result > 0 ? result : -EPIPE; + pos_data += bytes; + if (pos_data == chan->setup.pos_boundary) { + ctrl->pos_data = 0; + ctrl->frag_data = 0; + } else { + ctrl->pos_data = pos_data; + ctrl->frag_data = pos_data / chan->setup.frags; + } + } + offset += bytes; + count -= bytes; + result += bytes; + } + + return result; +} + +static int transfer_read(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + char *buf = data; + unsigned int v, voices; +#define COPY_LABELS +#include "plugin/plugin_ops.h" +#undef COPY_LABELS + void *copy; + snd_pcm_voice_setup_t *vsetup; + int idx; + size_t vsize, ssize; + idx = copy_index(chan->setup.format.format); + if (idx < 0) + return idx; + copy = copy_labels[idx]; + voices = chan->setup.format.voices; + vsetup = chan->voices_setup; + vsize = snd_pcm_format_size(chan->setup.format.format, 1); + ssize = vsize * chan->setup.format.voices; + hwoff /= ssize; + size /= ssize; + for (v = 0; v < voices; ++v, ++vsetup) { + const char *src; + size_t src_step; + char *dst; + size_t samples = size; + if (vsetup->first % 8 != 0 || + vsetup->step % 8 != 0) + return -EINVAL; + src_step = vsetup->step / 8; + src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; + dst = buf + off + v * vsize; + while (samples-- > 0) { + goto *copy; +#define COPY_END after +#include "plugin/plugin_ops.h" +#undef COPY_END + after: + src += src_step; + dst += ssize; + } + } + return 0; +} + +ssize_t snd_pcm_mmap_read(snd_pcm_t *pcm, void *buffer, size_t count) +{ + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (count > 0 && !buffer) + return -EFAULT; + if (!chan->setup.format.interleave && chan->setup.format.voices > 1) + return -EINVAL; + return snd_pcm_mmap_read1(pcm, buffer, count, transfer_read); +} + +static int transfer_readv(snd_pcm_t *pcm, size_t hwoff, void* data, size_t off, size_t size) +{ + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + struct iovec *vec = data; + unsigned int v, voices; +#define COPY_LABELS +#include "plugin/plugin_ops.h" +#undef COPY_LABELS + void *copy; + snd_pcm_voice_setup_t *vsetup; + int idx; + size_t ssize; + idx = copy_index(chan->setup.format.format); + if (idx < 0) + return idx; + copy = copy_labels[idx]; + voices = chan->setup.format.voices; + vsetup = chan->voices_setup; + ssize = snd_pcm_format_size(chan->setup.format.format, chan->setup.format.voices); + hwoff /= ssize; + size /= ssize; + off /= voices; + for (v = 0; v < voices; ++v, ++vsetup, ++vec) { + const char *src; + size_t src_step; + char *dst; + size_t samples = size; + if (vsetup->first % 8 != 0 || + vsetup->step % 8 != 0) + return -EINVAL; + src_step = vsetup->step / 8; + src = chan->mmap_data + vsetup->addr + (vsetup->first + vsetup->step * hwoff) / 8; + dst = vec->iov_base + off; + while (samples-- > 0) { + goto *copy; +#define COPY_END after +#include "plugin/plugin_ops.h" +#undef COPY_END + after: + src += src_step; + dst += ssize; + } + } + return 0; +} + +ssize_t snd_pcm_mmap_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long vcount) +{ + struct snd_pcm_chan *chan; + size_t result = 0; + unsigned int b; + if (!pcm) + return -EFAULT; + chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + if (!chan->open || !chan->valid_setup || !chan->valid_voices_setup) + return -EBADFD; + if (!chan->mmap_data || !chan->mmap_control) + return -EBADFD; + if (vcount > 0 && !vector) + return -EFAULT; + if (chan->setup.format.interleave) { + for (b = 0; b < vcount; b++) { + int ret; + ret = snd_pcm_mmap_read1(pcm, vector[b].iov_base, vector[b].iov_len, transfer_write); + if (ret < 0) + return result > 0 ? result : ret; + result += ret; + } + } else { + unsigned int voices = chan->setup.format.voices; + unsigned long bcount; + if (vcount % voices) + return -EINVAL; + bcount = vcount / voices; + for (b = 0; b < bcount; b++) { + unsigned int v; + int ret; + size_t count = 0; + count = vector[0].iov_len; + for (v = 0; v < voices; ++v) { + if (vector[v].iov_len != count) + return -EINVAL; + } + ret = snd_pcm_mmap_read1(pcm, (void *) vector, count * voices, transfer_readv); + if (ret < 0) + return result > 0 ? result : ret; + result += ret; + if ((size_t)ret != count * voices) + break; + vector += voices; + } + } + return result; +} + +static void *playback_mmap(void *d) +{ + snd_pcm_t *pcm = d; + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + snd_pcm_mmap_control_t *control; + char *data; + int frags; + int frag_size, voice_size, voice_frag_size; + int voices; + control = chan->mmap_control; + data = chan->mmap_data; + frags = chan->setup.frags; + frag_size = chan->setup.frag_size; + voices = chan->setup.format.voices; + voice_size = chan->mmap_data_size / voices; + voice_frag_size = voice_size / frags; + while (1) { + int err; + struct pollfd pfd; + unsigned int f, frag; + if (chan->mmap_thread_stop) + break; + + pthread_mutex_lock(&chan->mutex); + if (control->status != SND_PCM_STATUS_RUNNING) { + pthread_cond_wait(&chan->status_cond, &chan->mutex); + pthread_mutex_unlock(&chan->mutex); + continue; + } + pthread_mutex_unlock(&chan->mutex); + + pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_PLAYBACK); + pfd.events = POLLOUT | POLLERR; + err = poll(&pfd, 1, -1); + if (err < 0) { + fprintf(stderr, "poll err=%d\n", err); + continue; + } + if (pfd.revents & POLLERR) { + snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK, -1); + fprintf(stderr, "pollerr %d\n", control->status); + continue; + } + + frag = control->frag_io; + if (snd_pcm_mmap_playback_frags_used(pcm) <= 0) { + fprintf(stderr, "underrun\n"); + usleep(10000); + continue; + } + f = frag % frags; + if (chan->setup.format.interleave) { + err = snd_pcm_write(pcm, data + f * frag_size, frag_size); + } else { + struct iovec vector[voices]; + struct iovec *v = vector; + int voice; + for (voice = 0; voice < voices; ++voice) { + v->iov_base = data + voice_size * voice + f * voice_frag_size; + v->iov_len = voice_frag_size; + v++; + } + err = snd_pcm_writev(pcm, vector, voice_frag_size); + } + if (err <= 0) { + fprintf(stderr, "write err=%d\n", err); + snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK, -1); + continue; + } + pthread_mutex_lock(&chan->mutex); + pthread_cond_signal(&chan->ready_cond); + pthread_mutex_unlock(&chan->mutex); + frag++; + if (frag == chan->setup.frag_boundary) + frag = 0; + control->frag_io = frag; + } + return 0; +} + +static void *capture_mmap(void *d) +{ + snd_pcm_t *pcm = d; + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + snd_pcm_mmap_control_t *control; + char *data; + int frags; + int frag_size, voice_size, voice_frag_size; + int voices; + control = chan->mmap_control; + data = chan->mmap_data; + frags = chan->setup.frags; + frag_size = chan->setup.frag_size; + voices = chan->setup.format.voices; + voice_size = chan->mmap_data_size / voices; + voice_frag_size = voice_size / frags; + while (1) { + int err; + struct pollfd pfd; + unsigned int f, frag; + if (chan->mmap_thread_stop) + break; + + pthread_mutex_lock(&chan->mutex); + if (control->status != SND_PCM_STATUS_RUNNING) { + pthread_cond_wait(&chan->status_cond, &chan->mutex); + pthread_mutex_unlock(&chan->mutex); + continue; + } + pthread_mutex_unlock(&chan->mutex); + + pfd.fd = snd_pcm_file_descriptor(pcm, SND_PCM_CHANNEL_CAPTURE); + pfd.events = POLLIN | POLLERR; + err = poll(&pfd, 1, -1); + if (err < 0) { + fprintf(stderr, "poll err=%d\n", err); + continue; + } + if (pfd.revents & POLLERR) { + snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_CAPTURE, -1); + fprintf(stderr, "pollerr %d\n", control->status); + continue; + } + + frag = control->frag_io; + if (snd_pcm_mmap_capture_frags_free(pcm) <= 0) { + fprintf(stderr, "overrun\n"); + usleep(10000); + continue; + } + f = frag % frags; + if (chan->setup.format.interleave) { + err = snd_pcm_read(pcm, data + f * frag_size, frag_size); + } else { + struct iovec vector[voices]; + struct iovec *v = vector; + int voice; + for (voice = 0; voice < voices; ++voice) { + v->iov_base = data + voice_size * voice + f * voice_frag_size; + v->iov_len = voice_frag_size; + v++; + } + err = snd_pcm_readv(pcm, vector, voice_frag_size); + } + if (err < 0) { + fprintf(stderr, "read err=%d\n", err); + snd_pcm_mmap_status_change(pcm, SND_PCM_CHANNEL_CAPTURE, -1); + continue; + } + frag++; + if (frag == chan->setup.frag_boundary) + frag = 0; + control->frag_io = frag; + } + return 0; +} + +int snd_pcm_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control) +{ + struct snd_pcm_chan *chan; + snd_pcm_channel_info_t info; + size_t csize; + int err; + if (!pcm || !control) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (chan->mmap_control) { + *control = chan->mmap_control; + return 0; + } + if (!chan->valid_setup) + return -EBADFD; + csize = sizeof(snd_pcm_mmap_control_t); + + info.channel = channel; + err = snd_pcm_channel_info(pcm, &info); + if (err < 0) + return err; + if (info.flags & SND_PCM_CHNINFO_MMAP) { + if ((err = pcm->ops->mmap_control(pcm, channel, control, csize)) < 0) + return err; + } else { + *control = calloc(1, csize); + chan->mmap_control_emulation = 1; + } + chan->mmap_control = *control; + chan->mmap_control_size = csize; + return 0; +} + +int snd_pcm_mmap_data(snd_pcm_t *pcm, int channel, void **data) +{ + struct snd_pcm_chan *chan; + snd_pcm_channel_info_t info; + size_t bsize; + int err; + if (!pcm || !data) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (chan->mmap_data) { + *data = chan->mmap_data; + return 0; + } + if (!chan->valid_setup) + return -EBADFD; + + info.channel = channel; + err = snd_pcm_channel_info(pcm, &info); + if (err < 0) + return err; + bsize = info.mmap_size; + if (info.flags & SND_PCM_CHNINFO_MMAP) { + if ((err = pcm->ops->mmap_data(pcm, channel, data, bsize)) < 0) + return err; + } else { + *data = calloc(1, bsize); + + pthread_mutex_init(&chan->mutex, NULL); + pthread_cond_init(&chan->status_cond, NULL); + pthread_cond_init(&chan->ready_cond, NULL); + chan->mmap_thread_stop = 0; + if (channel == SND_PCM_CHANNEL_PLAYBACK) + err = pthread_create(&chan->mmap_thread, NULL, playback_mmap, pcm); + else + err = pthread_create(&chan->mmap_thread, NULL, capture_mmap, pcm); + if (err < 0) { + pthread_cond_destroy(&chan->status_cond); + pthread_cond_destroy(&chan->ready_cond); + pthread_mutex_destroy(&chan->mutex); + free(*data); + *data = 0; + return err; + } + chan->mmap_data_emulation = 1; + } + chan->mmap_data = *data; + chan->mmap_data_size = bsize; + snd_pcm_all_voices_setup(pcm, channel, NULL); + return 0; +} + +int snd_pcm_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **data) +{ + int err; + err = snd_pcm_mmap_control(pcm, channel, control); + if (err < 0) + return err; + err = snd_pcm_mmap_data(pcm, channel, data); + if (err < 0) { + snd_pcm_munmap_control(pcm, channel); + return err; + } + return 0; +} + +int snd_pcm_munmap_control(snd_pcm_t *pcm, int channel) +{ + int err; + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (!chan->mmap_control) + return -EINVAL; + if (chan->mmap_control_emulation) { + free(chan->mmap_control); + chan->mmap_control_emulation = 0; + } else { + if ((err = pcm->ops->munmap_control(pcm, channel, chan->mmap_control, chan->mmap_control_size)) < 0) + return err; + } + chan->mmap_control = 0; + chan->mmap_control_size = 0; + return 0; +} + +int snd_pcm_munmap_data(snd_pcm_t *pcm, int channel) +{ + int err; + struct snd_pcm_chan *chan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + chan = &pcm->chan[channel]; + if (!chan->open) + return -EBADFD; + if (!chan->mmap_data) + return -EINVAL; + if (chan->mmap_data_emulation) { + chan->mmap_thread_stop = 1; + pthread_mutex_lock(&chan->mutex); + pthread_cond_signal(&chan->status_cond); + pthread_mutex_unlock(&chan->mutex); + pthread_join(chan->mmap_thread, NULL); + pthread_cond_destroy(&chan->status_cond); + pthread_cond_destroy(&chan->ready_cond); + pthread_mutex_destroy(&chan->mutex); + free(chan->mmap_data); + chan->mmap_data_emulation = 0; + } else { + if ((err = pcm->ops->munmap_data(pcm, channel, chan->mmap_data, chan->mmap_data_size)) < 0) + return err; + } + chan->mmap_data = 0; + chan->mmap_data_size = 0; + return 0; +} + +int snd_pcm_munmap(snd_pcm_t *pcm, int channel) +{ + int err; + err = snd_pcm_munmap_control(pcm, channel); + if (err < 0) + return err; + return snd_pcm_munmap_data(pcm, channel); +} + diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c new file mode 100644 index 00000000..43cc1edd --- /dev/null +++ b/src/pcm/pcm_plug.c @@ -0,0 +1,824 @@ +/* + * PCM - Plug + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pcm_local.h" + +/* snd_pcm_plug helpers */ + +void *snd_pcm_plug_buf_alloc(snd_pcm_t *pcm, int channel, size_t size) +{ + int idx; + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_plug_chan *plugchan = &plug->chan[channel]; + + for (idx = 0; idx < 2; idx++) { + if (plugchan->alloc_lock[idx]) + continue; + if (plugchan->alloc_size[idx] >= size) { + plugchan->alloc_lock[idx] = 1; + return plugchan->alloc_ptr[idx]; + } + } + for (idx = 0; idx < 2; idx++) { + if (plugchan->alloc_lock[idx]) + continue; + if (plugchan->alloc_ptr[idx] != NULL) + free(plugchan->alloc_ptr[idx]); + plugchan->alloc_ptr[idx] = malloc(size); + if (plugchan->alloc_ptr[idx] == NULL) + return NULL; + plugchan->alloc_size[idx] = size; + plugchan->alloc_lock[idx] = 1; + return plugchan->alloc_ptr[idx]; + } + return NULL; +} + +void snd_pcm_plug_buf_unlock(snd_pcm_t *pcm, int channel, void *ptr) +{ + int idx; + + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + + if (!ptr) + return; + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + + for (idx = 0; idx < 2; idx++) { + if (plugchan->alloc_ptr[idx] == ptr) { + plugchan->alloc_lock[idx] = 0; + return; + } + } +} + +/* snd_pcm_plugin externs */ + +int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) +{ + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + snd_pcm_t *pcm; + if (!plugin) + return -EFAULT; + pcm = plugin->handle; + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[plugin->channel]; + plugin->next = plugchan->first; + plugin->prev = NULL; + if (plugchan->first) { + plugchan->first->prev = plugin; + plugchan->first = plugin; + } else { + plugchan->last = + plugchan->first = plugin; + } + return 0; +} + +int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) +{ + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + snd_pcm_t *pcm; + if (!plugin) + return -EFAULT; + pcm = plugin->handle; + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[plugin->channel]; + + plugin->next = NULL; + plugin->prev = plugchan->last; + if (plugchan->last) { + plugchan->last->next = plugin; + plugchan->last = plugin; + } else { + plugchan->last = + plugchan->first = plugin; + } + return 0; +} + +#if 0 +int snd_pcm_plugin_remove_to(snd_pcm_plugin_t *plugin) +{ + snd_pcm_plugin_t *plugin1, *plugin1_prev; + snd_pcm_plug_t *plug; + snd_pcm_t *pcm; + struct snd_pcm_plug_chan *plugchan; + if (!plugin) + return -EFAULT; + pcm = plugin->handle; + + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[plugin->channel]; + + plugin1 = plugin; + while (plugin1->prev) + plugin1 = plugin1->prev; + if (plugchan->first != plugin1) + return -EINVAL; + plugchan->first = plugin; + plugin1 = plugin->prev; + plugin->prev = NULL; + while (plugin1) { + plugin1_prev = plugin1->prev; + snd_pcm_plugin_free(plugin1); + plugin1 = plugin1_prev; + } + return 0; +} + +int snd_pcm_plug_remove_first(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + if (!pcm->chan[channel].open) + return -EBADFD; + + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + + plugin = plugchan->first; + if (plugin->next) { + plugin = plugin->next; + } else { + return snd_pcm_plug_clear(pcm, channel); + } + return snd_pcm_plugin_remove_to(plugin); +} +#endif + +/* snd_pcm_plug externs */ + +int snd_pcm_plug_clear(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plugin_t *plugin, *plugin_next; + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + int idx; + + if (!pcm) + return -EINVAL; + if (channel < 0 || channel > 1) + return -EINVAL; + if (!pcm->chan[channel].open) + return -EBADFD; + + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + plugin = plugchan->first; + plugchan->first = NULL; + plugchan->last = NULL; + while (plugin) { + plugin_next = plugin->next; + snd_pcm_plugin_free(plugin); + plugin = plugin_next; + } + for (idx = 0; idx < 2; idx++) { + if (plugchan->alloc_ptr[idx]) { + free(plugchan->alloc_ptr[idx]); + plugchan->alloc_ptr[idx] = 0; + } + plugchan->alloc_size[idx] = 0; + plugchan->alloc_lock[idx] = 0; + } + return 0; +} + +snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + if (!pcm) + return NULL; + if (channel < 0 || channel > 1) + return NULL; + if (!pcm->chan[channel].open) + return NULL; + + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + + return plugchan->first; +} + +snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + if (!pcm) + return NULL; + if (channel < 0 || channel > 1) + return NULL; + if (!pcm->chan[channel].open) + return NULL; + + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + + return plugchan->last; +} + +int snd_pcm_plug_direct(snd_pcm_t *pcm, int channel) +{ + return snd_pcm_plug_first(pcm, channel) == NULL; +} + +#if 0 +double snd_pcm_plug_client_ratio(snd_pcm_t *pcm, int channel) +{ + ssize_t client; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + if (!pcm->chan[channel].open) + return -EBADFD; + + client = snd_pcm_plug_client_size(pcm, channel, 1000000); + if (client < 0) + return 0; + return (double)client / (double)1000000; +} + +double snd_pcm_plug_slave_ratio(snd_pcm_t *pcm, int channel) +{ + ssize_t slave; + if (!pcm) + return -EFAULT; + if (channel < 0 || channel > 1) + return -EINVAL; + if (!pcm->chan[channel].open) + return -EBADFD; + + slave = snd_pcm_plug_slave_size(pcm, channel, 1000000); + if (slave < 0) + return 0; + return (double)slave / (double)1000000; +} +#endif + +/* + * + */ + +static int snd_pcm_plug_channel_close(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + snd_pcm_plug_clear(pcm, channel); + if (plug->close_slave) + return snd_pcm_channel_close(plug->slave, channel); + return 0; +} + +static int snd_pcm_plug_channel_nonblock(snd_pcm_t *pcm, int channel, int nonblock) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + return snd_pcm_channel_nonblock(plug->slave, channel, nonblock); +} + +static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + return snd_pcm_info(plug->slave, info); +} + +static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) +{ + int err; + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_chan *chan; + + if ((err = snd_pcm_channel_info(plug->slave, info)) < 0) + return err; + info->formats = snd_pcm_plug_formats(info->formats); + info->min_rate = 4000; + info->max_rate = 192000; + info->min_voices = 1; + info->max_voices = 32; + info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000; + info->flags |= SND_PCM_CHNINFO_INTERLEAVE | SND_PCM_CHNINFO_NONINTERLEAVE; + + chan = &pcm->chan[info->channel]; + if (pcm->chan[info->channel].valid_setup) { + info->buffer_size = snd_pcm_plug_client_size(pcm, info->channel, info->buffer_size); + info->min_fragment_size = snd_pcm_plug_client_size(pcm, info->channel, info->min_fragment_size); + info->max_fragment_size = snd_pcm_plug_client_size(pcm, info->channel, info->max_fragment_size); + info->fragment_align = snd_pcm_plug_client_size(pcm, info->channel, info->fragment_align); + info->fifo_size = snd_pcm_plug_client_size(pcm, info->channel, info->fifo_size); + info->transfer_block_size = snd_pcm_plug_client_size(pcm, info->channel, info->transfer_block_size); + if (chan->setup.mode == SND_PCM_MODE_BLOCK) + info->mmap_size = chan->setup.buffer_size; + else + info->mmap_size = snd_pcm_plug_client_size(pcm, info->channel, info->mmap_size); + } + if (!snd_pcm_plug_direct(pcm, info->channel)) + info->flags &= ~(SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_MMAP_VALID); + return 0; +} + +static int snd_pcm_plug_action(snd_pcm_t *pcm, int channel, int action, + unsigned long data) +{ + snd_pcm_plugin_t *plugin; + int err; + snd_pcm_plug_t *plug; + struct snd_pcm_plug_chan *plugchan; + plug = (snd_pcm_plug_t*) &pcm->private; + plugchan = &plug->chan[channel]; + + plugin = plugchan->first; + while (plugin) { + if (plugin->action) { + if ((err = plugin->action(plugin, action, data))<0) + return err; + } + plugin = plugin->next; + } + return 0; +} + +static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) +{ + snd_pcm_channel_params_t slave_params, params1; + snd_pcm_channel_info_t slave_info; + snd_pcm_plugin_t *plugin; + snd_pcm_plug_t *plug; + int err; + int channel = params->channel; + + plug = (snd_pcm_plug_t*) &pcm->private; + + /* + * try to decide, if a conversion is required + */ + + memset(&slave_info, 0, sizeof(slave_info)); + slave_info.channel = channel; + if ((err = snd_pcm_channel_info(plug->slave, &slave_info)) < 0) { + snd_pcm_plug_clear(pcm, channel); + return err; + } + + if ((err = snd_pcm_plug_slave_params(params, &slave_info, &slave_params)) < 0) + return err; + + + snd_pcm_plug_clear(pcm, channel); + + /* add necessary plugins */ + memcpy(¶ms1, params, sizeof(*params)); + if ((err = snd_pcm_plug_format(pcm, ¶ms1, &slave_params)) < 0) + return err; + + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_channel_params(plug->slave, params); + + /* + * I/O plugins + */ + + if (params->mode == SND_PCM_MODE_STREAM) { + pdprintf("params stream plugin\n"); + err = snd_pcm_plugin_build_stream(pcm, channel, plug->slave, &slave_params.format, &plugin); + } else if (params->mode == SND_PCM_MODE_BLOCK) { + if (slave_info.flags & SND_PCM_CHNINFO_MMAP) { + pdprintf("params mmap plugin\n"); + err = snd_pcm_plugin_build_mmap(pcm, channel, plug->slave, &slave_params.format, &plugin); + } else { + pdprintf("params block plugin\n"); + err = snd_pcm_plugin_build_block(pcm, channel, plug->slave, &slave_params.format, &plugin); + } + } else { + return -EINVAL; + } + if (err < 0) + return err; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + err = snd_pcm_plugin_append(plugin); + } else { + err = snd_pcm_plugin_insert(plugin); + } + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + + /* compute right sizes */ + slave_params.frag_size = snd_pcm_plug_slave_size(pcm, channel, slave_params.frag_size); + if (params->mode == SND_PCM_MODE_STREAM) { + slave_params.buf.stream.bytes_fill_max = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_fill_max); + slave_params.buf.stream.bytes_min = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_min); + slave_params.buf.stream.bytes_xrun_max = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_xrun_max); + slave_params.buf.stream.bytes_align = snd_pcm_plug_slave_size(pcm, channel, slave_params.buf.stream.bytes_align); + } else if (params->mode != SND_PCM_MODE_BLOCK) + return -EINVAL; + pdprintf("params requested params: format = %i, rate = %i, voices = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.voices); + err = snd_pcm_channel_params(plug->slave, &slave_params); + if (err < 0) + return err; + + err = snd_pcm_plug_action(pcm, channel, INIT, 0); + if (err < 0) + return err; + return 0; +} + +static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) +{ + int err; + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_plug_chan *plugchan; + + err = snd_pcm_channel_setup(plug->slave, setup); + if (err < 0) + return err; + if (snd_pcm_plug_direct(pcm, setup->channel)) + return 0; + setup->buffer_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->buffer_size); + setup->frag_size = snd_pcm_plug_client_size(pcm, setup->channel, setup->frag_size); + /* FIXME: it may overflow */ + setup->pos_boundary = snd_pcm_plug_client_size(pcm, setup->channel, setup->pos_boundary); + if (setup->mode == SND_PCM_MODE_STREAM) { + setup->buf.stream.bytes_min = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_min); + setup->buf.stream.bytes_align = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_align); + setup->buf.stream.bytes_xrun_max = snd_pcm_plug_client_size(pcm, setup->channel, setup->buf.stream.bytes_xrun_max); + } else if (setup->mode != SND_PCM_MODE_BLOCK) { + return -EINVAL; + } + + plugchan = &plug->chan[setup->channel]; + if (setup->channel == SND_PCM_CHANNEL_PLAYBACK) + setup->format = plugchan->first->src_format; + else + setup->format = plugchan->last->dst_format; + return 0; +} + +static int snd_pcm_plug_channel_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) +{ + int err; + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + + err = snd_pcm_channel_status(plug->slave, status); + if (err < 0) + return err; + if (snd_pcm_plug_direct(pcm, status->channel)) + return 0; + + /* FIXME: may overflow */ + status->pos_io = snd_pcm_plug_client_size(pcm, status->channel, status->pos_io); + status->pos_data = snd_pcm_plug_client_size(pcm, status->channel, status->pos_data); + status->bytes_used = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_used); + status->bytes_free = snd_pcm_plug_client_size(pcm, status->channel, status->bytes_free); + return 0; +} + +static int snd_pcm_plug_channel_prepare(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int err; + err = snd_pcm_channel_prepare(plug->slave, channel); + if (err < 0) + return err; + if (snd_pcm_plug_direct(pcm, channel)) + return 0; + if ((err = snd_pcm_plug_action(pcm, channel, PREPARE, 0))<0) + return err; + return 0; +} + +static int snd_pcm_plug_channel_go(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + return snd_pcm_channel_go(plug->slave, channel); +} + +static int snd_pcm_plug_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + return snd_pcm_sync_go(plug->slave, sync); +} + +static int snd_pcm_plug_channel_drain(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int err; + + if ((err = snd_pcm_channel_drain(plug->slave, channel)) < 0) + return err; + if (snd_pcm_plug_direct(pcm, channel)) + return 0; + if ((err = snd_pcm_plug_action(pcm, channel, DRAIN, 0))<0) + return err; + return 0; +} + +static int snd_pcm_plug_channel_flush(snd_pcm_t *pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int err; + + if ((err = snd_pcm_channel_flush(plug->slave, channel)) < 0) + return err; + if (snd_pcm_plug_direct(pcm, channel)) + return 0; + if ((err = snd_pcm_plug_action(pcm, channel, FLUSH, 0))<0) + return err; + return 0; +} + +static int snd_pcm_plug_channel_pause(snd_pcm_t *pcm, int channel, int enable) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int err; + + if ((err = snd_pcm_channel_pause(plug->slave, channel, enable)) < 0) + return err; + if ((err = snd_pcm_plug_action(pcm, channel, PAUSE, 0))<0) + return err; + return 0; +} + +static int snd_pcm_plug_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_chan *chan; + unsigned int voice; + int width; + size_t size; + + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_voice_setup(plug->slave, channel, setup); + + voice = setup->voice; + memset(setup, 0, sizeof(*setup)); + setup->voice = voice; + chan = &pcm->chan[channel]; + if (!chan->mmap_control) { + setup->addr = -1; + return 0; + } + if (voice >= chan->setup.format.voices) + return -EINVAL; + + width = snd_pcm_format_physical_width(chan->setup.format.format); + if (width < 0) + return width; + size = chan->mmap_data_size; + if (chan->setup.format.interleave) { + setup->addr = 0; + setup->first = voice * width; + setup->step = chan->setup.format.voices * width; + } else { + size /= chan->setup.format.voices; + setup->addr = setup->voice * size; + setup->first = 0; + setup->step = width; + } + return 0; +} + +ssize_t snd_pcm_plug_writev(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; + unsigned int k, step, voices; + int size = 0; + if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_PLAYBACK)) + return snd_pcm_writev(plug->slave, vector, count); + voices = chan->setup.format.voices; + if (chan->setup.format.interleave) + step = 1; + else { + step = voices; + if (count % voices != 0) + return -EINVAL; + } + for (k = 0; k < count; k += step, vector += step) { + snd_pcm_plugin_voice_t *voices; + int expected, ret; + expected = snd_pcm_plug_client_voices_iovec(pcm, SND_PCM_CHANNEL_PLAYBACK, vector, count, &voices); + if (expected < 0) + return expected; + ret = snd_pcm_plug_write_transfer(pcm, voices, expected); + if (ret < 0) { + if (size > 0) + return size; + return ret; + } + size += ret; + if (ret != expected) + return size; + } + return size; +} + +ssize_t snd_pcm_plug_readv(snd_pcm_t *pcm, const struct iovec *vector, unsigned long count) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; + unsigned int k, step, voices; + int size = 0; + if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_CAPTURE)) + return snd_pcm_readv(plug->slave, vector, count); + voices = chan->setup.format.voices; + if (chan->setup.format.interleave) + step = 1; + else { + step = voices; + if (count % voices != 0) + return -EINVAL; + } + for (k = 0; k < count; k += step) { + snd_pcm_plugin_voice_t *voices; + int expected, ret; + expected = snd_pcm_plug_client_voices_iovec(pcm, SND_PCM_CHANNEL_CAPTURE, vector, count, &voices); + if (expected < 0) + return expected; + ret = snd_pcm_plug_read_transfer(pcm, voices, expected); + if (ret < 0) { + if (size > 0) + return size; + return ret; + } + size += ret; + if (ret != expected) + return size; + } + return size; +} + +ssize_t snd_pcm_plug_write(snd_pcm_t *pcm, const void *buf, size_t count) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int expected; + snd_pcm_plugin_voice_t *voices; + + if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_PLAYBACK)) + return snd_pcm_write(plug->slave, buf, count); + expected = snd_pcm_plug_client_voices_buf(pcm, SND_PCM_CHANNEL_PLAYBACK, (char *)buf, count, &voices); + if (expected < 0) + return expected; + return snd_pcm_plug_write_transfer(pcm, voices, expected); +} + +ssize_t snd_pcm_plug_read(snd_pcm_t *pcm, void *buf, size_t count) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + int expected; + snd_pcm_plugin_voice_t *voices; + + if (snd_pcm_plug_direct(pcm, SND_PCM_CHANNEL_CAPTURE)) + return snd_pcm_read(plug->slave, buf, count); + expected = snd_pcm_plug_client_voices_buf(pcm, SND_PCM_CHANNEL_CAPTURE, buf, count, &voices); + if (expected < 0) + return expected; + return snd_pcm_plug_read_transfer(pcm, voices, expected); +} + +static int snd_pcm_plug_mmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, size_t csize UNUSED) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_mmap_control(plug->slave, channel, control); + return -EBADFD; +} + +static int snd_pcm_plug_mmap_data(snd_pcm_t *pcm, int channel, void **buffer, size_t bsize UNUSED) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_mmap_data(plug->slave, channel, buffer); + return -EBADFD; +} + +static int snd_pcm_plug_munmap_control(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t *control UNUSED, size_t csize UNUSED) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_munmap_control(plug->slave, channel); + return -EBADFD; +} + +static int snd_pcm_plug_munmap_data(snd_pcm_t *pcm, int channel, void *buffer UNUSED, size_t size UNUSED) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_munmap_data(plug->slave, channel); + return -EBADFD; +} + +static int snd_pcm_plug_voices_mask(snd_pcm_t *pcm, int channel, + bitset_t *client_vmask) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + if (snd_pcm_plug_direct(pcm, channel)) + return snd_pcm_voices_mask(plug->slave, channel, client_vmask); + if (channel == SND_PCM_CHANNEL_PLAYBACK) + return snd_pcm_plug_playback_voices_mask(pcm, client_vmask); + else + return snd_pcm_plug_capture_voices_mask(pcm, client_vmask); +} + +int snd_pcm_plug_file_descriptor(snd_pcm_t* pcm, int channel) +{ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &pcm->private; + return snd_pcm_file_descriptor(plug->slave, channel); +} + +struct snd_pcm_ops snd_pcm_plug_ops = { + channel_close: snd_pcm_plug_channel_close, + channel_nonblock: snd_pcm_plug_channel_nonblock, + info: snd_pcm_plug_info, + channel_info: snd_pcm_plug_channel_info, + channel_params: snd_pcm_plug_channel_params, + channel_setup: snd_pcm_plug_channel_setup, + voice_setup: snd_pcm_plug_voice_setup, + channel_status: snd_pcm_plug_channel_status, + channel_prepare: snd_pcm_plug_channel_prepare, + channel_go: snd_pcm_plug_channel_go, + sync_go: snd_pcm_plug_sync_go, + channel_drain: snd_pcm_plug_channel_drain, + channel_flush: snd_pcm_plug_channel_flush, + channel_pause: snd_pcm_plug_channel_pause, + write: snd_pcm_plug_write, + writev: snd_pcm_plug_writev, + read: snd_pcm_plug_read, + readv: snd_pcm_plug_readv, + mmap_control: snd_pcm_plug_mmap_control, + mmap_data: snd_pcm_plug_mmap_data, + munmap_control: snd_pcm_plug_munmap_control, + munmap_data: snd_pcm_plug_munmap_data, + file_descriptor: snd_pcm_plug_file_descriptor, + voices_mask: snd_pcm_plug_voices_mask, +}; + +int snd_pcm_plug_connect(snd_pcm_t **handle, snd_pcm_t *slave, int mode, int close_slave) +{ + snd_pcm_t *pcm; + snd_pcm_plug_t *plug; + int err; + err = snd_pcm_abstract_open(handle, mode, SND_PCM_TYPE_PLUG, sizeof(snd_pcm_plug_t)); + if (err < 0) { + if (close_slave) + snd_pcm_close(slave); + return err; + } + pcm = *handle; + pcm->ops = &snd_pcm_plug_ops; + plug = (snd_pcm_plug_t*) &pcm->private; + plug->slave = slave; + plug->close_slave = close_slave; + return 0; +} + +int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int mode) +{ + snd_pcm_t *slave; + int err; + err = snd_pcm_open_subdevice(&slave, card, device, subdevice, mode); + if (err < 0) + return err; + return snd_pcm_plug_connect(handle, slave, mode, 1); +} + +int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int mode) +{ + return snd_pcm_plug_open_subdevice(handle, card, device, -1, mode); +} + + diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c deleted file mode 100644 index 3952bd0a..00000000 --- a/src/pcm/pcm_plugin.c +++ /dev/null @@ -1,1192 +0,0 @@ -/* - * PCM Plug-In Interface - * Copyright (c) 1999 by Jaroslav Kysela - * - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "pcm_local.h" - -static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size); -static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr); - -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; - 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_physical_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_physical_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); - plugin->src_voices = snd_pcm_plugin_src_voices; - plugin->dst_voices = snd_pcm_plugin_dst_voices; - return plugin; -} - -int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) -{ - if (plugin) { - if (plugin->private_free) - plugin->private_free(plugin, plugin->private_data); - if (plugin->name) - free(plugin->name); - free(plugin); - } - return 0; -} - -int snd_pcm_plugin_clear(snd_pcm_t *pcm, int channel) -{ - snd_pcm_plugin_t *plugin, *plugin_next; - struct snd_pcm_plug *plug; - int idx; - - if (!pcm) - return -EINVAL; - plugin = pcm->chan[channel].plug.first; - pcm->chan[channel].plug.first = NULL; - pcm->chan[channel].plug.last = NULL; - while (plugin) { - plugin_next = plugin->next; - snd_pcm_plugin_free(plugin); - plugin = plugin_next; - } - plug = &pcm->chan[channel].plug; - for (idx = 0; idx < 2; idx++) { - if (plug->alloc_ptr[idx]) - free(plug->alloc_ptr[idx]); - plug->alloc_ptr[idx] = 0; - plug->alloc_size[idx] = 0; - plug->alloc_lock[idx] = 0; - } - return 0; -} - -int snd_pcm_plugin_insert(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin) -{ - if (!pcm || channel < 0 || channel > 1 || !plugin) - return -EINVAL; - plugin->next = pcm->chan[channel].plug.first; - plugin->prev = NULL; - if (pcm->chan[channel].plug.first) { - pcm->chan[channel].plug.first->prev = plugin; - pcm->chan[channel].plug.first = plugin; - } else { - pcm->chan[channel].plug.last = - pcm->chan[channel].plug.first = plugin; - } - return 0; -} - -int snd_pcm_plugin_append(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin) -{ - if (!pcm || channel < 0 || channel > 1 || !plugin) - return -EINVAL; - plugin->next = NULL; - plugin->prev = pcm->chan[channel].plug.last; - if (pcm->chan[channel].plug.last) { - pcm->chan[channel].plug.last->next = plugin; - pcm->chan[channel].plug.last = plugin; - } else { - pcm->chan[channel].plug.last = - pcm->chan[channel].plug.first = plugin; - } - return 0; -} - -int snd_pcm_plugin_remove_to(snd_pcm_t *pcm, int channel, snd_pcm_plugin_t *plugin) -{ - snd_pcm_plugin_t *plugin1, *plugin1_prev; - - if (!pcm || channel < 0 || channel > 1 || !plugin || !plugin->prev) - return -EINVAL; - plugin1 = plugin; - while (plugin1->prev) - plugin1 = plugin1->prev; - if (pcm->chan[channel].plug.first != plugin1) - return -EINVAL; - pcm->chan[channel].plug.first = plugin; - plugin1 = plugin->prev; - plugin->prev = NULL; - while (plugin1) { - plugin1_prev = plugin1->prev; - snd_pcm_plugin_free(plugin1); - plugin1 = plugin1_prev; - } - return 0; -} - -int snd_pcm_plugin_remove_first(snd_pcm_t *pcm, int channel) -{ - snd_pcm_plugin_t *plugin; - - plugin = pcm->chan[channel].plug.first; - if (plugin->next) { - plugin = plugin->next; - } else { - return snd_pcm_plugin_clear(pcm, channel); - } - return snd_pcm_plugin_remove_to(pcm, channel, plugin); -} - -snd_pcm_plugin_t *snd_pcm_plugin_first(snd_pcm_t *pcm, int channel) -{ - if (!pcm || channel < 0 || channel > 1) - return NULL; - return pcm->chan[channel].plug.first; -} - -snd_pcm_plugin_t *snd_pcm_plugin_last(snd_pcm_t *pcm, int channel) -{ - if (!pcm || channel < 0 || channel > 1) - return NULL; - return pcm->chan[channel].plug.last; -} - - -double snd_pcm_plugin_transfer_ratio(snd_pcm_t *pcm, int channel) -{ - ssize_t transfer; - - transfer = snd_pcm_plugin_client_size(pcm, channel, 1000000); - if (transfer < 0) - return 0; - return (double)transfer / (double)1000000; -} - -double snd_pcm_plugin_hardware_ratio(snd_pcm_t *pcm, int channel) -{ - ssize_t hardware; - - hardware = snd_pcm_plugin_hardware_size(pcm, channel, 1000000); - if (hardware < 0) - return 0; - return (double)hardware / (double)1000000; -} - -/* - * - */ - -int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) -{ - int err; - - if ((err = snd_pcm_channel_info(pcm, info)) < 0) - return err; - info->formats = snd_pcm_plugin_formats(info->formats); - info->min_rate = 4000; - info->max_rate = 192000; - info->min_voices = 1; - info->max_voices = 32; - info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000; - info->buffer_size = snd_pcm_plugin_client_size(pcm, info->channel, info->buffer_size); - info->min_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->min_fragment_size); - info->max_fragment_size = snd_pcm_plugin_client_size(pcm, info->channel, info->max_fragment_size); - info->fragment_align = snd_pcm_plugin_client_size(pcm, info->channel, info->fragment_align); - info->fifo_size = snd_pcm_plugin_client_size(pcm, info->channel, info->fifo_size); - info->transfer_block_size = snd_pcm_plugin_client_size(pcm, info->channel, info->transfer_block_size); - info->mmap_size = snd_pcm_plugin_client_size(pcm, info->channel, info->mmap_size); - return 0; -} - -static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action, - unsigned long data) -{ - snd_pcm_plugin_t *plugin; - int err; - - plugin = pcm->chan[channel].plug.first; - while (plugin) { - if (plugin->action) { - if ((err = plugin->action(plugin, action, data))<0) - return err; - } - plugin = plugin->next; - } - return 0; -} - -int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) -{ - snd_pcm_channel_params_t hwparams, params1; - snd_pcm_channel_info_t hwinfo; - snd_pcm_plugin_t *plugin; - struct snd_pcm_plug *plug; - int err; - - if (!pcm || !params || params->channel < 0 || params->channel > 1) - return -EINVAL; - - plug = &pcm->chan[params->channel].plug; - if (plug->mmap_data) - return -EBADFD; - - /* - * try to decide, if a conversion is required - */ - - memset(&hwinfo, 0, sizeof(hwinfo)); - hwinfo.channel = params->channel; - if ((err = snd_pcm_channel_info(pcm, &hwinfo)) < 0) { - snd_pcm_plugin_clear(pcm, params->channel); - return err; - } - - if ((err = snd_pcm_plugin_hwparams(params, &hwinfo, &hwparams)) < 0) - return err; - - - snd_pcm_plugin_clear(pcm, params->channel); - - /* add necessary plugins */ - memcpy(¶ms1, params, sizeof(*params)); - if ((err = snd_pcm_plugin_format(pcm, ¶ms1, &hwparams, &hwinfo)) < 0) - return err; - - if (snd_pcm_plugin_first(pcm, params->channel) == NULL) - return snd_pcm_channel_params(pcm, params); - - /* - * I/O plugins - */ - - if (params->mode == SND_PCM_MODE_STREAM) { - pdprintf("params stream plugin\n"); - 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, &hwparams.format, &plugin); - } else { - pdprintf("params block plugin\n"); - err = snd_pcm_plugin_build_block(pcm, params->channel, &hwparams.format, &plugin); - } - } else { - return -EINVAL; - } - if (err < 0) - return err; - if (params->channel == SND_PCM_CHANNEL_PLAYBACK) { - err = snd_pcm_plugin_append(pcm, params->channel, plugin); - } else { - err = snd_pcm_plugin_insert(pcm, params->channel, plugin); - } - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - - /* compute right sizes */ - if (params->mode == SND_PCM_MODE_STREAM) { - pdprintf("params queue_size = %i\n", hwparams.buf.stream.queue_size); - hwparams.buf.stream.queue_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.queue_size); - hwparams.buf.stream.max_fill = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.stream.max_fill); - pdprintf("params queue_size = %i\n", hwparams.buf.stream.queue_size); - } else if (params->mode == SND_PCM_MODE_BLOCK) { - pdprintf("params frag_size = %i\n", hwparams.buf.block.frag_size); - hwparams.buf.block.frag_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.block.frag_size); - pdprintf("params frag_size = %i\n", hwparams.buf.block.frag_size); - } else { - return -EINVAL; - } - pdprintf("params requested params: format = %i, rate = %i, voices = %i\n", hwparams.format.format, hwparams.format.rate, hwparams.format.voices); - err = snd_pcm_channel_params(pcm, &hwparams); - if (err < 0) - return err; - - plug->setup_is_valid = 0; - memset(&plug->setup, 0, sizeof(snd_pcm_channel_setup_t)); - plug->setup.channel = params->channel; - if ((err = snd_pcm_plugin_setup(pcm, &plug->setup))<0) - return err; - err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT, (long)&hwparams); - if (err < 0) - return err; - return 0; -} - -int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) -{ - int err; - struct snd_pcm_plug *plug; - - if (!pcm || !setup || setup->channel < 0 || setup->channel > 1) - return -EINVAL; - plug = &pcm->chan[setup->channel].plug; - if (plug->first == NULL) - return snd_pcm_channel_setup(pcm, setup); - if (plug->setup_is_valid) { - memcpy(setup, &plug->setup, sizeof(*setup)); - return 0; - } - err = snd_pcm_channel_setup(pcm, setup); - if (err < 0) - 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_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_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; - } - if (setup->channel == SND_PCM_CHANNEL_PLAYBACK) - setup->format = plug->first->src_format; - else - setup->format = plug->last->dst_format; - memcpy(&plug->setup, setup, sizeof(*setup)); - plug->setup_is_valid = 1; - return 0; -} - -int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status) -{ - int err; - - if (!pcm || !status || status->channel < 0 || status->channel > 1) - return -EINVAL; - err = snd_pcm_channel_status(pcm, status); - if (err < 0) - return err; - /* 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; -} - -static void mmap_clear(struct snd_pcm_plug *plug) -{ - int idx; - snd_pcm_mmap_fragment_t *f; - - f = plug->mmap_control->fragments; - for (idx = 0; idx < plug->setup.buf.block.frags; idx++) { - f->data = 0; - f->io = 0; - f->res[0] = 0; - f->res[1] = 0; - f++; - } - plug->mmap_control->status.frag_io = 0; - plug->mmap_control->status.block = 0; - plug->mmap_control->status.expblock = 0; -} - -static void snd_pcm_plugin_status_change(snd_pcm_t *pcm, int channel) -{ - struct snd_pcm_chan *chan; - struct snd_pcm_plug *plug; - snd_pcm_channel_status_t status; - int newstatus; - - chan = &pcm->chan[channel]; - plug = &chan->plug; - if (!plug->mmap_data) - return; - if (chan->mmap_control) { - newstatus = chan->mmap_control->status.status; - } else { - status.channel = channel; - if (snd_pcm_channel_status(pcm, &status) < 0) - newstatus = SND_PCM_STATUS_NOTREADY; - else - newstatus = status.status; - } - if (plug->mmap_control->status.status == SND_PCM_STATUS_RUNNING && - newstatus != SND_PCM_STATUS_RUNNING) { - mmap_clear(plug); - } - plug->mmap_control->status.status = newstatus; -} - -int snd_pcm_plugin_prepare(snd_pcm_t *pcm, int channel) -{ - int err; - - if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE, 0))<0) - return err; - if ((err = snd_pcm_channel_prepare(pcm, channel)) < 0) - return err; - snd_pcm_plugin_status_change(pcm, channel); - return 0; -} - -int snd_pcm_plugin_go(snd_pcm_t *pcm, int channel) -{ - struct snd_pcm_chan *chan; - struct snd_pcm_plug *plug; - if (!pcm || channel < 0 || channel > 1) - return -EINVAL; - chan = &pcm->chan[channel]; - plug = &chan->plug; - if (plug->first == NULL) - return snd_pcm_channel_go(pcm, channel); - if (plug->mmap_control) { - if (plug->mmap_control->status.status != SND_PCM_STATUS_PREPARED) - return -EBADFD; - if (channel == SND_PCM_CHANNEL_PLAYBACK) { - if (!plug->mmap_control->fragments[0].data) - return -EIO; - } else { - if (plug->mmap_control->fragments[0].data) - return -EIO; - } - plug->mmap_control->status.status = SND_PCM_STATUS_RUNNING; - } - return 0; -} - -int snd_pcm_plugin_sync_go(snd_pcm_t *pcm, snd_pcm_sync_t *sync) -{ - int err; - if (!pcm || !sync) - return -EINVAL; - if (snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK) || - snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) { - if ((err = snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_PLAYBACK)) < 0) - return err; - return snd_pcm_plugin_go(pcm, SND_PCM_CHANNEL_CAPTURE); - } - return snd_pcm_sync_go(pcm, sync); -} - -int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm) -{ - int err; - - if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN, 0))<0) - return err; - if ((err = snd_pcm_playback_drain(pcm)) < 0) - return err; - snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK); - return 0; -} - -int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel) -{ - int err; - - pdprintf("flush\n"); - if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH, 0))<0) - return err; - if ((err = snd_pcm_channel_flush(pcm, channel)) < 0) - return err; - snd_pcm_plugin_status_change(pcm, channel); - return 0; -} - -int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable) -{ - int err; - - if ((err = snd_pcm_playback_pause(pcm, enable)) < 0) - return err; - snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK); - return 0; -} - -ssize_t snd_pcm_plugin_transfer_size(snd_pcm_t *pcm, int channel) -{ - struct snd_pcm_plug *plug; - if (!pcm || channel < 0 || channel > 1) - return -EINVAL; - plug = &pcm->chan[channel].plug; - if (plug->first == NULL) - return snd_pcm_plugin_transfer_size(pcm, channel); - if (!plug->setup_is_valid) - return -EBADFD; - if (plug->setup.mode != SND_PCM_MODE_BLOCK) - return -EBADFD; - return plug->setup.buf.block.frag_size; -} - -int snd_pcm_plugin_voice_setup(snd_pcm_t *pcm, int channel, snd_pcm_voice_setup_t *setup) -{ - int voice, width, size; - struct snd_pcm_plug* plug; - - if (!pcm || !setup) - return -EINVAL; - if (channel < 0 || channel > 1) - return -EINVAL; - voice = setup->voice; - memset(setup, 0, sizeof(*setup)); - setup->voice = voice; - if (voice < 0) - return -EINVAL; - plug = &pcm->chan[channel].plug; - if (plug->first == NULL) - return snd_pcm_voice_setup(pcm, channel, setup); - if (!plug->mmap_control) { - setup->addr = -1; - return 0; - } - if (voice >= plug->setup.format.voices) - return -EINVAL; - - width = snd_pcm_format_physical_width(plug->setup.format.format); - if (width < 0) - return width; - size = plug->mmap_size; - if (plug->setup.format.interleave) { - setup->addr = 0; - setup->first = voice * width; - setup->step = plug->setup.format.voices * width; - } else { - size /= plug->setup.format.voices; - setup->addr = setup->voice * size; - setup->first = 0; - setup->step = width; - } - return 0; -} - -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->first = voice * width; - v->step = 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->first = 0; - v->step = 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); -} - -static ssize_t snd_pcm_plugin_writev1(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; - ssize_t samples; - ssize_t size; - int idx, err; - - plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK); - 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; - samples = snd_pcm_plugin_src_size_to_samples(plugin, size); - if (samples < 0) - return samples; - while (plugin) { - if ((next = plugin->next) != NULL) { - ssize_t samples1 = samples; - if (plugin->dst_samples) - samples1 = plugin->dst_samples(plugin, samples); - if ((err = next->src_voices(next, &dst_voices, samples1)) < 0) { - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); - return err; - } - } else { - dst_voices = NULL; - } - pdprintf("write plugin: %s, %i\n", plugin->name, samples); - if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) { - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); - if (dst_voices) - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, dst_voices->aptr); - return samples; - } - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); - plugin = plugin->next; - src_voices = dst_voices; - } - samples = snd_pcm_plugin_client_samples(pcm, SND_PCM_CHANNEL_PLAYBACK, samples); - size = snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK), samples); - if (size < 0) - return size; - pdprintf("writev result = %i\n", size); - return size; -} - -ssize_t snd_pcm_plugin_writev(snd_pcm_t *pcm, const struct iovec *vector, int count) -{ - snd_pcm_plugin_t *plugin; - int k, step, voices; - int size = 0; - if (vector == NULL) - return -EFAULT; - plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK); - if (plugin == NULL) - return snd_pcm_readv(pcm, vector, count); - voices = plugin->src_format.voices; - if (plugin->src_format.interleave) - step = 1; - else { - step = voices; - if (count % voices != 0) - return -EINVAL; - } - for (k = 0; k < count; k += step) { - int expected = 0; - int j; - int ret = snd_pcm_plugin_writev1(pcm, vector, step); - if (ret < 0) { - if (size > 0) - return size; - return ret; - } - size += ret; - for (j = 0; j < step; ++j) { - expected += vector->iov_len; - ++vector; - } - if (ret != expected) - return size; - } - return size; -} - -static ssize_t snd_pcm_plugin_readv1(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; - ssize_t samples; - ssize_t size; - int idx, err; - - plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE); - 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(snd_pcm_plugin_last(pcm, 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 ((err = plugin->dst_voices(plugin, &dst_voices, samples)) < 0) { - if (src_voices) - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, 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, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); - return err; - } - } - pdprintf("read plugin: %s, %i\n", plugin->name, samples); - if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) { - if (src_voices) - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr); - return samples; - } - if (src_voices) - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); - plugin = plugin->next; - src_voices = dst_voices; - } - snd_pcm_plugin_buf_free(pcm, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr); - size = snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE), samples); - pdprintf("readv 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; - int k, step, voices; - int size = 0; - if (vector == NULL) - return -EFAULT; - plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE); - if (plugin == NULL) - return snd_pcm_readv(pcm, vector, count); - voices = plugin->dst_format.voices; - if (plugin->dst_format.interleave) - step = 1; - else { - step = voices; - if (count % voices != 0) - return -EINVAL; - } - for (k = 0; k < count; k += step) { - int expected = 0; - int j; - int ret = snd_pcm_plugin_readv1(pcm, vector, step); - if (ret < 0) { - if (size > 0) - return size; - return ret; - } - size += ret; - for (j = 0; j < step; ++j) { - expected += vector->iov_len; - ++vector; - } - if (ret != expected) - return size; - } - return size; -} - -ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count) -{ - snd_pcm_plugin_t *plugin; - int voices; - - if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_PLAYBACK)) == NULL) - return snd_pcm_write(pcm, buffer, count); - voices = plugin->src_format.voices; - if (count % voices != 0) - return -EINVAL; - if (plugin->src_format.interleave) { - struct iovec vec; - vec.iov_base = (void *)buffer; - vec.iov_len = count; - return snd_pcm_plugin_writev1(pcm, &vec, 1); - } else { - int idx; - 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_writev1(pcm, vec, voices); - } -} - -ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count) -{ - snd_pcm_plugin_t *plugin; - int voices; - - if ((plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL) - return snd_pcm_read(pcm, buffer, count); - voices = plugin->dst_format.voices; - if (count % voices != 0) - return -EINVAL; - if (plugin->dst_format.interleave) { - struct iovec vec; - vec.iov_base = buffer; - vec.iov_len = count; - return snd_pcm_plugin_readv1(pcm, &vec, 1); - } else { - int idx; - 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_readv1(pcm, vec, voices); - } -} - -/* - * Plugin helpers - */ - -static void *snd_pcm_plugin_buf_alloc(snd_pcm_t *pcm, int channel, size_t size) -{ - int idx; - void *ptr; - struct snd_pcm_plug *plug; - - if (pcm == NULL || size <= 0) - return NULL; - plug = &pcm->chan[channel].plug; - for (idx = 0; idx < 2; idx++) { - if (plug->alloc_lock[idx]) - continue; - if (plug->alloc_ptr[idx] == NULL) - continue; - if (plug->alloc_size[idx] >= size) { - plug->alloc_lock[idx] = 1; - return plug->alloc_ptr[idx]; - } - } - for (idx = 0; idx < 2; idx++) { - if (plug->alloc_lock[idx]) - continue; - if (plug->alloc_ptr[idx] == NULL) - continue; - ptr = realloc(plug->alloc_ptr[idx], size); - if (ptr == NULL) - continue; - plug->alloc_size[idx] = size; - plug->alloc_lock[idx] = 1; - return plug->alloc_ptr[idx] = ptr; - } - for (idx = 0; idx < 2; idx++) { - if (plug->alloc_ptr[idx] != NULL) - continue; - ptr = malloc(size); - if (ptr == NULL) - continue; - plug->alloc_size[idx] = size; - plug->alloc_lock[idx] = 1; - return plug->alloc_ptr[idx] = ptr; - } - return NULL; -} - -static void snd_pcm_plugin_buf_free(snd_pcm_t *pcm, int channel, void *ptr) -{ - int idx; - - struct snd_pcm_plug *plug; - - if (pcm == NULL || ptr == NULL) - return; - plug = &pcm->chan[channel].plug; - for (idx = 0; idx < 2; idx++) { - if (plug->alloc_ptr[idx] == ptr) { - plug->alloc_lock[idx] = 0; - return; - } - } -} - -static int snd_pcm_plugin_xvoices(snd_pcm_plugin_t *plugin, - int channel, - snd_pcm_plugin_voice_t **voices, - size_t samples, - snd_pcm_format_t *format) -{ - char *ptr; - int width, voice; - long size; - snd_pcm_plugin_voice_t *v; - - *voices = NULL; - if ((width = snd_pcm_format_physical_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, channel, 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->first = voice * width; - v->step = format->voices * width; - } else { - v->addr = ptr + (voice * size); - v->first = 0; - v->step = width; - } - } - *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, SND_PCM_CHANNEL_PLAYBACK, 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, SND_PCM_CHANNEL_CAPTURE, voices, samples, &plugin->dst_format); -} - -static void *playback_mmap(void *data) -{ - snd_pcm_t *pcm = data; - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_PLAYBACK]; - struct snd_pcm_plug *plug = &chan->plug; - snd_pcm_mmap_control_t *control; - void *buffer; - int frags; - int frag_size, voice_size, voice_frag_size; - int voices; - control = plug->mmap_control; - buffer = plug->mmap_data; - frags = plug->setup.buf.block.frags; - frag_size = plug->setup.buf.block.frag_size; - voices = plug->setup.format.voices; - voice_size = plug->mmap_size / voices; - voice_frag_size = voice_size / frags; - while (1) { - int err; - struct pollfd pfd; - int frag = control->status.frag_io; - if (plug->thread_stop) - break; - if (control->status.status != SND_PCM_STATUS_RUNNING && - control->status.status != SND_PCM_STATUS_PREPARED) { - usleep(100000); - continue; - } - pfd.fd = chan->fd; - pfd.events = POLLOUT; - pfd.revents = 0; - err = poll(&pfd, 1, -1); - if (err < 0) { - fprintf(stderr, "error on poll\n"); - continue; - } - if (!control->fragments[frag].data) { - usleep(10000); - continue; - } - /* NYI: status.block */ - control->fragments[frag].io = 1; - if (plug->setup.format.interleave) { - err = snd_pcm_plugin_write(pcm, buffer + frag * frag_size, frag_size); - } else { - struct iovec vector[voices]; - struct iovec *v = vector; - int voice; - for (voice = 0; voice < voices; ++voice) { - v->iov_base = buffer + voice_size * voice + frag * voice_frag_size; - v->iov_len = voice_frag_size; - v++; - } - err = snd_pcm_plugin_writev(pcm, vector, voice_frag_size); - } - if (err <= 0) { - control->fragments[frag].io = 0; - snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_PLAYBACK); - continue; - } - control->fragments[frag].io = 0; - control->fragments[frag].data = 0; - frag++; - if (frag == frags) - frag = 0; - control->status.frag_io = frag; - } - return 0; -} - -static void *capture_mmap(void *data) -{ - snd_pcm_t *pcm = data; - struct snd_pcm_chan *chan = &pcm->chan[SND_PCM_CHANNEL_CAPTURE]; - struct snd_pcm_plug *plug = &chan->plug; - snd_pcm_mmap_control_t *control; - void *buffer; - int frag, frags; - int frag_size, voice_size, voice_frag_size; - int voices; - control = plug->mmap_control; - buffer = plug->mmap_data; - frag = 0; - frags = plug->setup.buf.block.frags; - frag_size = plug->setup.buf.block.frag_size; - voices = plug->setup.format.voices; - voice_size = plug->mmap_size / voices; - voice_frag_size = voice_size / frags; - while (1) { - int err; - struct pollfd pfd; - if (plug->thread_stop) - break; - if (control->status.status != SND_PCM_STATUS_RUNNING) { - usleep(100000); - continue; - } - pfd.fd = chan->fd; - pfd.events = POLLIN; - pfd.revents = 0; - err = poll(&pfd, 1, -1); - if (err < 0) { - printf("error on poll\n"); - continue; - } - if (control->fragments[frag].data) { - usleep(10000); - continue; - } - /* NYI: status.block */ - control->status.frag_io = frag; - control->fragments[frag].io = 1; - if (plug->setup.format.interleave) { - err = snd_pcm_plugin_read(pcm, buffer + frag * frag_size, frag_size); - } else { - struct iovec vector[voices]; - struct iovec *v = vector; - int voice; - for (voice = 0; voice < voices; ++voice) { - v->iov_base = buffer + voice_size * voice + frag * voice_frag_size; - v->iov_len = voice_frag_size; - v++; - } - err = snd_pcm_plugin_readv(pcm, vector, voice_frag_size); - } - if (err < 0) { - snd_pcm_plugin_status_change(pcm, SND_PCM_CHANNEL_CAPTURE); - continue; - } - control->fragments[frag].io = 0; - control->fragments[frag].data = 1; - frag++; - if (frag == frags) - frag = 0; - } - return 0; -} - -int snd_pcm_plugin_mmap(snd_pcm_t *pcm, int channel, snd_pcm_mmap_control_t **control, void **data) -{ - int err; - - struct snd_pcm_plug *plug; - if (!pcm || channel < 0 || channel > 1 || !control || !data) - return -EINVAL; - - plug = &pcm->chan[channel].plug; - if (plug->first == NULL) - return snd_pcm_mmap(pcm, channel, control, data); - if (!plug->setup_is_valid) - return -EBADFD; - if (plug->setup.mode != SND_PCM_MODE_BLOCK) - return -EINVAL; - - if (plug->mmap_data) { - *control = plug->mmap_control; - *data = plug->mmap_data; - return 0; - } - - plug->mmap_control = malloc(sizeof(snd_pcm_mmap_control_t)); - plug->mmap_size = plug->setup.buf.block.frag_size * plug->setup.buf.block.frags; - plug->mmap_data = malloc(plug->mmap_size); - - mmap_clear(plug); - - *control = plug->mmap_control; - *data = plug->mmap_data; - - plug->thread_stop = 0; - if (channel == SND_PCM_CHANNEL_PLAYBACK) - err = pthread_create(&plug->thread, NULL, playback_mmap, pcm); - else - err = pthread_create(&plug->thread, NULL, capture_mmap, pcm); - if (err < 0) { - snd_pcm_plugin_munmap(pcm, channel); - return err; - } - return 0; -} - -int snd_pcm_plugin_munmap(snd_pcm_t *pcm, int channel) -{ - struct snd_pcm_plug *plug; - if (!pcm || channel < 0 || channel > 1) - return -EINVAL; - plug = &pcm->chan[channel].plug; - if (plug->first == NULL) - return snd_pcm_munmap(pcm, channel); - if (plug->mmap_data) { - plug->thread_stop = 1; - pthread_join(plug->thread, NULL); - free(plug->mmap_control); - plug->mmap_control = 0; - free(plug->mmap_data); - plug->mmap_data = 0; - } - return 0; -} diff --git a/src/pcm/pcm_plugin_build.c b/src/pcm/pcm_plugin_build.c index d186daa4..99d51513 100644 --- a/src/pcm/pcm_plugin_build.c +++ b/src/pcm/pcm_plugin_build.c @@ -1,6 +1,7 @@ /* * PCM Plug-In shared (kernel/library) code * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 2000 by Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify @@ -23,31 +24,189 @@ #define PLUGIN_DEBUG #endif #ifdef __KERNEL__ -#include "../include/driver.h" -#include "../include/pcm.h" -#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) +#include "../../include/driver.h" +#include "../../include/pcm.h" +#define snd_pcm_plug_first(handle, channel) ((handle)->runtime->oss.plugin_first) +#define snd_pcm_plug_last(handle, channel) ((handle)->runtime->oss.plugin_last) #else #include #include #include #include +#include #include "pcm_local.h" -#define my_calloc(size) calloc(1, size) -#define my_free(ptr) free(ptr) -#define my_strdup(str) strdup(str) #endif +static int snd_pcm_plugin_src_voices_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + bitset_t *vmask = plugin->src_vmask; + bitset_copy(vmask, dst_vmask, plugin->src_format.voices); + *src_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_dst_voices_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + bitset_t *vmask = plugin->dst_vmask; + bitset_copy(vmask, src_vmask, plugin->dst_format.voices); + *dst_vmask = vmask; + return 0; +} + +static int snd_pcm_plugin_side_voices(snd_pcm_plugin_t *plugin, + int client_side, + size_t samples, + snd_pcm_plugin_voice_t **voices) +{ + char *ptr; + int width; + unsigned int voice; + long size; + snd_pcm_plugin_voice_t *v; + snd_pcm_format_t *format; + if ((plugin->channel == SND_PCM_CHANNEL_PLAYBACK && client_side) || + (plugin->channel == SND_PCM_CHANNEL_CAPTURE && !client_side)) { + format = &plugin->src_format; + v = plugin->src_voices; + } else { + format = &plugin->dst_format; + v = plugin->dst_voices; + } + + *voices = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + size = format->voices * samples * width; + if ((size % 8) != 0) + return -EINVAL; + size /= 8; + ptr = (char *)snd_pcm_plug_buf_alloc(plugin->handle, plugin->channel, size); + if (ptr == NULL) + return -ENOMEM; + if ((size % format->voices) != 0) + return -EINVAL; + size /= format->voices; + for (voice = 0; voice < format->voices; voice++, v++) { + v->enabled = 1; + v->wanted = 0; + v->aptr = ptr; + if (format->interleave) { + v->addr = ptr; + v->first = voice * width; + v->step = format->voices * width; + } else { + v->addr = ptr + (voice * size); + v->first = 0; + v->step = width; + } + } + return 0; +} + +int snd_pcm_plugin_client_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) +{ + return snd_pcm_plugin_side_voices(plugin, 1, samples, voices); +} + +int snd_pcm_plugin_slave_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) +{ + return snd_pcm_plugin_side_voices(plugin, 0, samples, voices); +} + + +int snd_pcm_plugin_build(snd_pcm_plugin_handle_t *handle, + int channel, + const char *name, + snd_pcm_format_t *src_format, + snd_pcm_format_t *dst_format, + int extra, + snd_pcm_plugin_t **ret) +{ + snd_pcm_plugin_t *plugin; + + if (!handle) + return -EFAULT; + if (extra < 0) + return -EINVAL; + if (channel < 0 || channel > 1) + return -EINVAL; + if (!src_format || !dst_format) + return -EFAULT; + plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + extra); + if (plugin == NULL) + return -ENOMEM; + plugin->name = name ? strdup(name) : NULL; + plugin->handle = handle; + plugin->channel = channel; + memcpy(&plugin->src_format, src_format, sizeof(snd_pcm_format_t)); + if ((plugin->src_width = snd_pcm_format_physical_width(src_format->format)) < 0) + return -EINVAL; + memcpy(&plugin->dst_format, dst_format, sizeof(snd_pcm_format_t)); + if ((plugin->dst_width = snd_pcm_format_physical_width(dst_format->format)) < 0) + return -EINVAL; + plugin->src_voices = calloc(src_format->voices, sizeof(snd_pcm_plugin_voice_t)); + if (plugin->src_voices == NULL) { + free(plugin); + return -ENOMEM; + } + plugin->dst_voices = calloc(dst_format->voices, sizeof(snd_pcm_plugin_voice_t)); + if (plugin->dst_voices == NULL) { + free(plugin->src_voices); + free(plugin); + return -ENOMEM; + } + plugin->src_vmask = bitset_alloc(src_format->voices); + if (plugin->src_vmask == NULL) { + free(plugin->src_voices); + free(plugin->dst_voices); + free(plugin); + return -ENOMEM; + } + plugin->dst_vmask = bitset_alloc(dst_format->voices); + if (plugin->dst_vmask == NULL) { + free(plugin->src_voices); + free(plugin->dst_voices); + free(plugin->src_vmask); + free(plugin); + return -ENOMEM; + } + plugin->client_voices = snd_pcm_plugin_client_voices; + plugin->src_voices_mask = snd_pcm_plugin_src_voices_mask; + plugin->dst_voices_mask = snd_pcm_plugin_dst_voices_mask; + *ret = plugin; + return 0; +} + +int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) +{ + if (plugin) { + if (plugin->private_free) + plugin->private_free(plugin, plugin->private_data); + if (plugin->name) + free(plugin->name); + free(plugin->src_voices); + free(plugin->dst_voices); + free(plugin->src_vmask); + free(plugin->dst_vmask); + free(plugin); + } + return 0; +} + ssize_t snd_pcm_plugin_src_samples_to_size(snd_pcm_plugin_t *plugin, size_t samples) { ssize_t result; if (plugin == NULL) - return -EINVAL; + return -EFAULT; result = samples * plugin->src_format.voices * plugin->src_width; #if 0 if ((result % 8) != 0) @@ -61,7 +220,7 @@ ssize_t snd_pcm_plugin_dst_samples_to_size(snd_pcm_plugin_t *plugin, size_t samp ssize_t result; if (plugin == NULL) - return -EINVAL; + return -EFAULT; result = samples * plugin->dst_format.voices * plugin->dst_width; #if 0 if ((result % 8) != 0) @@ -76,7 +235,7 @@ ssize_t snd_pcm_plugin_src_size_to_samples(snd_pcm_plugin_t *plugin, size_t size long tmp; if (plugin == NULL) - return -EINVAL; + return -EFAULT; result = size * 8; tmp = plugin->src_format.voices * plugin->src_width; #if 0 @@ -92,7 +251,7 @@ ssize_t snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_t *plugin, size_t size long tmp; if (plugin == NULL) - return -EINVAL; + return -EFAULT; result = size * 8; tmp = plugin->dst_format.voices * plugin->dst_width; #if 0 @@ -102,19 +261,19 @@ ssize_t snd_pcm_plugin_dst_size_to_samples(snd_pcm_plugin_t *plugin, size_t size return result / tmp; } -ssize_t snd_pcm_plugin_client_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_samples) +ssize_t snd_pcm_plug_client_samples(snd_pcm_plugin_handle_t *handle, 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)) + if (handle == NULL) + return -EFAULT; + if (channel != SND_PCM_CHANNEL_PLAYBACK && + 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); + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK); while (plugin && drv_samples > 0) { plugin_prev = plugin->prev; if (plugin->src_samples) @@ -122,7 +281,7 @@ ssize_t snd_pcm_plugin_client_samples(snd_pcm_plugin_handle_t *pb, int channel, plugin = plugin_prev; } } else if (channel == SND_PCM_CHANNEL_CAPTURE) { - plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE); + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); while (plugin && drv_samples > 0) { plugin_next = plugin->next; if (plugin->dst_samples) @@ -133,111 +292,127 @@ ssize_t snd_pcm_plugin_client_samples(snd_pcm_plugin_handle_t *pb, int channel, return drv_samples; } -ssize_t snd_pcm_plugin_hardware_samples(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_samples) +ssize_t snd_pcm_plug_slave_samples(snd_pcm_plugin_handle_t *handle, int channel, size_t clt_samples) { snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; + ssize_t samples; - if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && - channel != SND_PCM_CHANNEL_CAPTURE)) + if (handle == NULL) + return -EFAULT; + if (channel != SND_PCM_CHANNEL_PLAYBACK && + channel != SND_PCM_CHANNEL_CAPTURE) return -EINVAL; if (clt_samples == 0) return 0; - if (clt_samples < 0) - return -EINVAL; + samples = clt_samples; if (channel == SND_PCM_CHANNEL_PLAYBACK) { - plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK); - while (plugin && clt_samples > 0) { + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK); + while (plugin && samples > 0) { plugin_next = plugin->next; - if (plugin->dst_samples) - clt_samples = plugin->dst_samples(plugin, clt_samples); + if (plugin->dst_samples) { + samples = plugin->dst_samples(plugin, samples); + if (samples < 0) + return 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); + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE); while (plugin) { plugin_prev = plugin->prev; - if (plugin->src_samples) - clt_samples = plugin->src_samples(plugin, clt_samples); + if (plugin->src_samples) { + samples = plugin->src_samples(plugin, samples); + if (samples < 0) + return samples; + } plugin = plugin_prev; } } - return clt_samples; + return samples; } -ssize_t snd_pcm_plugin_client_size(snd_pcm_plugin_handle_t *pb, int channel, size_t drv_size) +ssize_t snd_pcm_plug_client_size(snd_pcm_plugin_handle_t *handle, 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)) + if (handle == NULL) + return -EFAULT; + if (channel != SND_PCM_CHANNEL_PLAYBACK && + channel != SND_PCM_CHANNEL_CAPTURE) return -EINVAL; if (drv_size == 0) return 0; - if (drv_size < 0) - return -EINVAL; if (channel == SND_PCM_CHANNEL_PLAYBACK) { - plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_PLAYBACK); + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK); if (plugin == NULL) return drv_size; result = snd_pcm_plugin_dst_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_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK); result = snd_pcm_plugin_src_samples_to_size(plugin, result); } else if (channel == SND_PCM_CHANNEL_CAPTURE) { - plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_CAPTURE); + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); if (plugin == NULL) return drv_size; 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); + if (result < 0) + return result; + result = snd_pcm_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE); result = snd_pcm_plugin_dst_samples_to_size(plugin, result); } return result; } -ssize_t snd_pcm_plugin_hardware_size(snd_pcm_plugin_handle_t *pb, int channel, size_t clt_size) +ssize_t snd_pcm_plug_slave_size(snd_pcm_plugin_handle_t *handle, int channel, size_t clt_size) { snd_pcm_plugin_t *plugin; ssize_t result = 0; - if (pb == NULL || (channel != SND_PCM_CHANNEL_PLAYBACK && - channel != SND_PCM_CHANNEL_CAPTURE)) + if (handle == NULL) + return -EFAULT; + if (channel != SND_PCM_CHANNEL_PLAYBACK && + channel != SND_PCM_CHANNEL_CAPTURE) return -EINVAL; if (clt_size == 0) return 0; - if (clt_size < 0) - return -EINVAL; if (channel == SND_PCM_CHANNEL_PLAYBACK) { - plugin = snd_pcm_plugin_first(pb, SND_PCM_CHANNEL_PLAYBACK); + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK); if (plugin == NULL) return clt_size; 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_plug_slave_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK); result = snd_pcm_plugin_dst_samples_to_size(plugin, result); } else if (channel == SND_PCM_CHANNEL_CAPTURE) { - plugin = snd_pcm_plugin_last(pb, SND_PCM_CHANNEL_CAPTURE); + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE); if (plugin == NULL) return clt_size; result = snd_pcm_plugin_dst_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_plug_slave_samples(handle, SND_PCM_CHANNEL_PLAYBACK, result); + if (result < 0) + return result; + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); result = snd_pcm_plugin_src_samples_to_size(plugin, result); } return result; } -unsigned int snd_pcm_plugin_formats(unsigned int formats) +unsigned int snd_pcm_plug_formats(unsigned int formats) { int linfmts = (SND_PCM_FMT_U8 | SND_PCM_FMT_S8 | SND_PCM_FMT_U16_LE | SND_PCM_FMT_S16_LE | @@ -273,14 +448,14 @@ static int preferred_formats[] = { SND_PCM_SFMT_U8 }; -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_plug_slave_params(snd_pcm_channel_params_t *params, + snd_pcm_channel_info_t *slave_info, + snd_pcm_channel_params_t *slave_params) { - memcpy(hwparams, params, sizeof(*hwparams)); - if ((hwinfo->formats & (1 << params->format.format)) == 0) { + memcpy(slave_params, params, sizeof(*slave_params)); + if ((slave_info->formats & (1 << params->format.format)) == 0) { int format = params->format.format; - if ((snd_pcm_plugin_formats(hwinfo->formats) & (1 << format)) == 0) + if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0) return -EINVAL; if (snd_pcm_format_linear(format)) { int width = snd_pcm_format_width(format); @@ -296,7 +471,7 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, for (sgn = 0; sgn < 2; ++sgn) { format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); if (format1 >= 0 && - hwinfo->formats & (1 << format1)) + slave_info->formats & (1 << format1)) goto _found; unsignd1 = !unsignd1; } @@ -310,9 +485,9 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, } return -EINVAL; _found: - hwparams->format.format = format1; + slave_params->format.format = format1; } else { - int i; + unsigned int i; switch (format) { case SND_PCM_SFMT_MU_LAW: #ifndef __KERNEL__ @@ -321,8 +496,8 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, #endif for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { int format1 = preferred_formats[i]; - if (hwinfo->formats & (1 << format1)) { - hwparams->format.format = format1; + if (slave_info->formats & (1 << format1)) { + slave_params->format.format = format1; break; } } @@ -336,30 +511,26 @@ int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params, } /* voices */ - if (params->format.voices < hwinfo->min_voices || - params->format.voices > hwinfo->max_voices) { - int dst_voices = params->format.voices < hwinfo->min_voices ? - hwinfo->min_voices : hwinfo->max_voices; - if ((params->format.rate < hwinfo->min_rate || - params->format.rate > hwinfo->max_rate) && - dst_voices > 2) - dst_voices = 2; - hwparams->format.voices = dst_voices; + if (params->format.voices < slave_info->min_voices || + params->format.voices > slave_info->max_voices) { + unsigned int dst_voices = params->format.voices < slave_info->min_voices ? + slave_info->min_voices : slave_info->max_voices; + slave_params->format.voices = dst_voices; } /* rate */ - if (params->format.rate < hwinfo->min_rate || - params->format.rate > hwinfo->max_rate) { - int dst_rate = params->format.rate < hwinfo->min_rate ? - hwinfo->min_rate : hwinfo->max_rate; - hwparams->format.rate = dst_rate; + if (params->format.rate < slave_info->min_rate || + params->format.rate > slave_info->max_rate) { + unsigned int dst_rate = params->format.rate < slave_info->min_rate ? + slave_info->min_rate : slave_info->max_rate; + slave_params->format.rate = dst_rate; } /* interleave */ - if (!(hwinfo->flags & SND_PCM_CHNINFO_INTERLEAVE)) - hwparams->format.interleave = 0; - if (!(hwinfo->flags & SND_PCM_CHNINFO_NONINTERLEAVE)) - hwparams->format.interleave = 1; + if (!(slave_info->flags & SND_PCM_CHNINFO_INTERLEAVE)) + slave_params->format.interleave = 0; + if (!(slave_info->flags & SND_PCM_CHNINFO_NONINTERLEAVE)) + slave_params->format.interleave = 1; return 0; } @@ -373,10 +544,9 @@ typedef int ttable_entry_t; typedef float ttable_entry_t; #endif -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) +int snd_pcm_plug_format(snd_pcm_plugin_handle_t *handle, + snd_pcm_channel_params_t *params, + snd_pcm_channel_params_t *slave_params) { snd_pcm_channel_params_t tmpparams; snd_pcm_channel_params_t dstparams; @@ -386,14 +556,14 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, switch (params->channel) { case SND_PCM_CHANNEL_PLAYBACK: - memcpy(&dstparams, hwparams, sizeof(*hwparams)); - srcparams = hwparams; + memcpy(&dstparams, slave_params, sizeof(*slave_params)); + srcparams = slave_params; memcpy(srcparams, params, sizeof(*params)); break; case SND_PCM_CHANNEL_CAPTURE: memcpy(&dstparams, params, sizeof(*params)); srcparams = params; - memcpy(srcparams, hwparams, sizeof(*hwparams)); + memcpy(srcparams, slave_params, sizeof(*slave_params)); break; default: return -EINVAL; @@ -411,6 +581,9 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, dstparams.format.rate, dstparams.format.voices); + if (srcparams->format.voices == 1) + srcparams->format.interleave = dstparams.format.interleave; + /* Format change (linearization) */ if ((srcparams->format.format != dstparams.format.format || srcparams->format.rate != dstparams.format.rate || @@ -420,22 +593,26 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, tmpparams.format.format = dstparams.format.format; else tmpparams.format.format = SND_PCM_SFMT_S16; + tmpparams.format.interleave = dstparams.format.interleave; switch (srcparams->format.format) { case SND_PCM_SFMT_MU_LAW: - err = snd_pcm_plugin_build_mulaw(pb, + err = snd_pcm_plugin_build_mulaw(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); break; #ifndef __KERNEL__ case SND_PCM_SFMT_A_LAW: - err = snd_pcm_plugin_build_alaw(pb, + err = snd_pcm_plugin_build_alaw(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); break; case SND_PCM_SFMT_IMA_ADPCM: - err = snd_pcm_plugin_build_adpcm(pb, + err = snd_pcm_plugin_build_adpcm(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); @@ -447,7 +624,7 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, pdprintf("params format change: src=%i, dst=%i returns %i\n", srcparams->format.format, tmpparams.format.format, err); if (err < 0) return err; - err = snd_pcm_plugin_append(pb, params->channel, plugin); + err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -459,7 +636,7 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, if (srcparams->format.voices > dstparams.format.voices) { int sv = srcparams->format.voices; int dv = dstparams.format.voices; - ttable_entry_t *ttable = my_calloc(dv*sv*sizeof(*ttable)); + ttable_entry_t *ttable = calloc(1, dv*sv*sizeof(*ttable)); #if 1 if (sv == 2 && dv == 1) { ttable[0] = HALF; @@ -472,20 +649,23 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, ttable[v * sv + v] = FULL; } tmpparams.format.voices = dstparams.format.voices; + tmpparams.format.interleave = dstparams.format.interleave; if (srcparams->format.rate == dstparams.format.rate && snd_pcm_format_linear(dstparams.format.format)) tmpparams.format.format = dstparams.format.format; - err = snd_pcm_plugin_build_route(pb, &srcparams->format, + err = snd_pcm_plugin_build_route(handle, + params->channel, + &srcparams->format, &tmpparams.format, ttable, &plugin); - my_free(ttable); + free(ttable); pdprintf("params voices reduction: src=%i, dst=%i returns %i\n", srcparams->format.voices, tmpparams.format.voices, err); if (err < 0) { snd_pcm_plugin_free(plugin); return err; } - err = snd_pcm_plugin_append(pb, params->channel, plugin); + err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -496,10 +676,12 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, /* rate resampling */ if (srcparams->format.rate != dstparams.format.rate) { tmpparams.format.rate = dstparams.format.rate; + tmpparams.format.interleave = dstparams.format.interleave; if (srcparams->format.voices == dstparams.format.voices && snd_pcm_format_linear(dstparams.format.format)) tmpparams.format.format = dstparams.format.format; - err = snd_pcm_plugin_build_rate(pb, + err = snd_pcm_plugin_build_rate(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); @@ -508,7 +690,7 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, snd_pcm_plugin_free(plugin); return err; } - err = snd_pcm_plugin_append(pb, params->channel, plugin); + err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -520,33 +702,42 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, if (srcparams->format.voices < dstparams.format.voices) { int sv = srcparams->format.voices; int dv = dstparams.format.voices; - ttable_entry_t *ttable = my_calloc(dv * sv * sizeof(*ttable)); -#if 1 - if (sv == 1 && dv == 2) { - ttable[0] = FULL; - ttable[1] = FULL; - } else -#endif + ttable_entry_t *ttable = calloc(1, dv * sv * sizeof(*ttable)); +#if 0 { int v; for (v = 0; v < sv; ++v) ttable[v * sv + v] = FULL; } +#else + { + /* Playback is spreaded on all voices */ + int vd, vs; + for (vd = 0, vs = 0; vd < dv; ++vd) { + ttable[vd * sv + vs] = FULL; + vs++; + if (vs == sv) + vs = 0; + } + } +#endif tmpparams.format.voices = dstparams.format.voices; + tmpparams.format.interleave = dstparams.format.interleave; if (snd_pcm_format_linear(dstparams.format.format)) tmpparams.format.format = dstparams.format.format; - err = snd_pcm_plugin_build_route(pb, + err = snd_pcm_plugin_build_route(handle, + params->channel, &srcparams->format, &tmpparams.format, ttable, &plugin); - my_free(ttable); + free(ttable); pdprintf("params voices extension: src=%i, dst=%i returns %i\n", srcparams->format.voices, tmpparams.format.voices, err); if (err < 0) { snd_pcm_plugin_free(plugin); return err; } - err = snd_pcm_plugin_append(pb, params->channel, plugin); + err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -557,21 +748,25 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, /* format change */ if (srcparams->format.format != dstparams.format.format) { tmpparams.format.format = dstparams.format.format; + tmpparams.format.interleave = dstparams.format.interleave; if (tmpparams.format.format == SND_PCM_SFMT_MU_LAW) { - err = snd_pcm_plugin_build_mulaw(pb, + err = snd_pcm_plugin_build_mulaw(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); } #ifndef __KERNEL__ else if (tmpparams.format.format == SND_PCM_SFMT_A_LAW) { - err = snd_pcm_plugin_build_alaw(pb, + err = snd_pcm_plugin_build_alaw(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); } else if (tmpparams.format.format == SND_PCM_SFMT_IMA_ADPCM) { - err = snd_pcm_plugin_build_adpcm(pb, + err = snd_pcm_plugin_build_adpcm(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); @@ -579,7 +774,8 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, #endif else if (snd_pcm_format_linear(srcparams->format.format) && snd_pcm_format_linear(tmpparams.format.format)) { - err = snd_pcm_plugin_build_linear(pb, + err = snd_pcm_plugin_build_linear(handle, + params->channel, &srcparams->format, &tmpparams.format, &plugin); @@ -589,7 +785,25 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, pdprintf("params format change: src=%i, dst=%i returns %i\n", srcparams->format.format, tmpparams.format.format, err); if (err < 0) return err; - err = snd_pcm_plugin_append(pb, params->channel, plugin); + err = snd_pcm_plugin_append(plugin); + if (err < 0) { + snd_pcm_plugin_free(plugin); + return err; + } + srcparams->format = tmpparams.format; + } + + /* interleave */ + if (srcparams->format.interleave != dstparams.format.interleave) { + tmpparams.format.interleave = dstparams.format.interleave; + err = snd_pcm_plugin_build_copy(handle, + params->channel, + &tmpparams.format, + &plugin); + pdprintf("interleave change: src=%i, dst=%i returns %i\n", srcparams->format.interleave, tmpparams.format.interleave, err); + if (err < 0) + return err; + err = snd_pcm_plugin_append(plugin); if (err < 0) { snd_pcm_plugin_free(plugin); return err; @@ -599,3 +813,423 @@ int snd_pcm_plugin_format(snd_pcm_plugin_handle_t *pb, return 0; } + +ssize_t snd_pcm_plug_client_voices_buf(snd_pcm_plugin_handle_t *handle, + int channel, + char *buf, + size_t count, + snd_pcm_plugin_voice_t **voices) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_voice_t *v; + snd_pcm_format_t *format; + int width, nvoices, voice; + + if (buf == NULL) + return -EINVAL; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + plugin = snd_pcm_plug_first(handle, channel); + format = &plugin->src_format; + v = plugin->src_voices; + } + else { + plugin = snd_pcm_plug_last(handle, channel); + format = &plugin->dst_format; + v = plugin->dst_voices; + } + *voices = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + if ((count * 8) % width != 0) + return -EINVAL; + nvoices = format->voices; + if (format->interleave || + format->voices == 1) { + for (voice = 0; voice < nvoices; voice++, v++) { + v->enabled = 1; + v->wanted = (channel == SND_PCM_CHANNEL_CAPTURE); + v->aptr = NULL; + v->addr = buf; + v->first = voice * width; + v->step = nvoices * width; + } + return count; + } else + return -EINVAL; +} + +int snd_pcm_plug_client_voices_iovec(snd_pcm_plugin_handle_t *handle, + int channel, + const struct iovec *vector, + unsigned long count, + snd_pcm_plugin_voice_t **voices) +{ + snd_pcm_plugin_t *plugin; + snd_pcm_plugin_voice_t *v; + snd_pcm_format_t *format; + int width; + unsigned int nvoices, voice; + + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + plugin = snd_pcm_plug_first(handle, channel); + format = &plugin->src_format; + v = plugin->src_voices; + } + else { + plugin = snd_pcm_plug_last(handle, channel); + format = &plugin->dst_format; + v = plugin->dst_voices; + } + *voices = v; + if ((width = snd_pcm_format_physical_width(format->format)) < 0) + return width; + nvoices = format->voices; + if (format->interleave) { + if (count != 1 || vector->iov_base == NULL || + (vector->iov_len * 8) % width != 0) + return -EINVAL; + + for (voice = 0; voice < nvoices; voice++, v++) { + v->enabled = 1; + v->wanted = (channel == SND_PCM_CHANNEL_CAPTURE); + v->aptr = NULL; + v->addr = vector->iov_base; + v->first = voice * width; + v->step = nvoices * width; + } + return vector->iov_len; + } else { + size_t len; + if (count != nvoices) + return -EINVAL; + len = vector->iov_len; + if ((len * 8) % width != 0) + return -EINVAL; + for (voice = 0; voice < nvoices; voice++, v++, vector++) { + if (vector->iov_len != len) + return -EINVAL; + v->enabled = (vector->iov_base != NULL); + v->wanted = (v->enabled && (channel == SND_PCM_CHANNEL_CAPTURE)); + v->aptr = NULL; + v->addr = vector->iov_base; + v->first = 0; + v->step = width; + } + return len * nvoices; + } +} + +int snd_pcm_plug_playback_voices_mask(snd_pcm_plugin_handle_t *handle, + bitset_t *client_vmask) +{ +#ifndef __KERNEL__ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private; +#endif + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_PLAYBACK); + if (plugin == NULL) { +#ifndef __KERNEL__ + return snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_PLAYBACK, client_vmask); +#else + return 0; +#endif + } else { + int svoices = plugin->dst_format.voices; + bitset_t bs[bitset_size(svoices)]; + bitset_t *srcmask; + bitset_t *dstmask = bs; + int err; + bitset_one(dstmask, svoices); +#ifndef __KERNEL__ + err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_PLAYBACK, dstmask); + if (err < 0) + return err; +#endif + if (plugin == NULL) { + bitset_and(client_vmask, dstmask, svoices); + return 0; + } + while (1) { + err = plugin->src_voices_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + if (plugin->prev == NULL) + break; + plugin = plugin->prev; + } + bitset_and(client_vmask, dstmask, plugin->src_format.voices); + return 0; + } +} + +int snd_pcm_plug_capture_voices_mask(snd_pcm_plugin_handle_t *handle, + bitset_t *client_vmask) +{ +#ifndef __KERNEL__ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private; +#endif + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); + if (plugin == NULL) { +#ifndef __KERNEL__ + return snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, client_vmask); +#else + return 0; +#endif + } else { + int svoices = plugin->src_format.voices; + bitset_t bs[bitset_size(svoices)]; + bitset_t *srcmask = bs; + bitset_t *dstmask; + int err; + bitset_one(srcmask, svoices); +#ifndef __KERNEL__ + err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, srcmask); + if (err < 0) + return err; +#endif + while (1) { + err = plugin->dst_voices_mask(plugin, srcmask, &dstmask); + if (err < 0) + return err; + srcmask = dstmask; + if (plugin->next == NULL) + break; + plugin = plugin->next; + } + bitset_and(client_vmask, srcmask, plugin->dst_format.voices); + return 0; + } +} + +static int snd_pcm_plug_playback_disable_useless_voices(snd_pcm_plugin_handle_t *handle, + snd_pcm_plugin_voice_t *src_voices) +{ + snd_pcm_plugin_t *plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK); + unsigned int nvoices = plugin->src_format.voices; + bitset_t bs[bitset_size(nvoices)]; + bitset_t *srcmask = bs; + int err; + unsigned int voice; + for (voice = 0; voice < nvoices; voice++) { + if (src_voices[voice].enabled) + bitset_set(srcmask, voice); + else + bitset_reset(srcmask, voice); + } + err = snd_pcm_plug_playback_voices_mask(handle, srcmask); + if (err < 0) + return err; + for (voice = 0; voice < nvoices; voice++) { + if (!bitset_get(srcmask, voice)) + src_voices[voice].enabled = 0; + } + return 0; +} + +static int snd_pcm_plug_capture_disable_useless_voices(snd_pcm_plugin_handle_t *handle, + snd_pcm_plugin_voice_t *src_voices, + snd_pcm_plugin_voice_t *client_voices) +{ +#ifndef __KERNEL__ + snd_pcm_plug_t *plug = (snd_pcm_plug_t*) &handle->private; +#endif + snd_pcm_plugin_t *plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE); + unsigned int nvoices = plugin->dst_format.voices; + bitset_t bs[bitset_size(nvoices)]; + bitset_t *dstmask = bs; + bitset_t *srcmask; + int err; + unsigned int voice; + for (voice = 0; voice < nvoices; voice++) { + if (client_voices[voice].enabled) + bitset_set(dstmask, voice); + else + bitset_reset(dstmask, voice); + } + while (plugin) { + err = plugin->src_voices_mask(plugin, dstmask, &srcmask); + if (err < 0) + return err; + dstmask = srcmask; + plugin = plugin->prev; + } +#ifndef __KERNEL__ + err = snd_pcm_voices_mask(plug->slave, SND_PCM_CHANNEL_CAPTURE, dstmask); + if (err < 0) + return err; +#endif + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); + nvoices = plugin->src_format.voices; + for (voice = 0; voice < nvoices; voice++) { + if (!bitset_get(dstmask, voice)) + src_voices[voice].enabled = 0; + } + return 0; +} + +void snd_pcm_plugin_silence_voice(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *dst_voice, + size_t samples) +{ + char *dst = dst_voice->addr + dst_voice->first / 8; + int dst_step = dst_voice->step / 8; + switch (plugin->dst_width) { + case 4: { + u_int8_t silence = snd_pcm_format_silence_64(plugin->dst_format.format); + u_int8_t s0 = silence & 0x0f; + u_int8_t s1 = silence & 0xf0; + int dstbit = dst_voice->first % 8; + int dstbit_step = dst_voice->step % 8; + while (samples-- > 0) { + if (dstbit) { + *dst &= 0x0f; + *dst |= s1; + } else { + *dst &= 0xf0; + *dst |= s0; + } + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + break; + } + case 8: { + u_int8_t silence = snd_pcm_format_silence_64(plugin->dst_format.format); + while (samples-- > 0) { + *dst = silence; + dst += dst_step; + } + break; + } + case 16: { + u_int16_t silence = snd_pcm_format_silence_64(plugin->dst_format.format); + while (samples-- > 0) { + *(u_int16_t*)dst = silence; + dst += dst_step; + } + break; + } + case 32: { + u_int32_t silence = snd_pcm_format_silence_64(plugin->dst_format.format); + while (samples-- > 0) { + *(u_int32_t*)dst = silence; + dst += dst_step; + } + break; + } + case 64: { + u_int64_t silence = snd_pcm_format_silence_64(plugin->dst_format.format); + while (samples-- > 0) { + *(u_int64_t*)dst = silence; + dst += dst_step; + } + break; + } + } +} + + +ssize_t snd_pcm_plug_write_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *src_voices, size_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_voice_t *dst_voices; + ssize_t samples; + int err; + + if ((err = snd_pcm_plug_playback_disable_useless_voices(handle, src_voices)) < 0) + return err; + + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK); + samples = snd_pcm_plugin_src_size_to_samples(plugin, size); + if (samples < 0) + return samples; + while (plugin && samples > 0) { + if ((next = plugin->next) != NULL) { + ssize_t samples1 = samples; + if (plugin->dst_samples) + samples1 = plugin->dst_samples(plugin, samples); + if ((err = next->client_voices(next, samples1, &dst_voices)) < 0) { + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); + return err; + } + } else { + if ((err = snd_pcm_plugin_slave_voices(plugin, samples, &dst_voices)) < 0) + return err; + } + pdprintf("write plugin: %s, %i\n", plugin->name, samples); + if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) { + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, dst_voices->aptr); + return samples; + } + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); + src_voices = dst_voices; + plugin = next; + } + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_PLAYBACK, src_voices->aptr); + samples = snd_pcm_plug_client_samples(handle, SND_PCM_CHANNEL_PLAYBACK, samples); + if (samples < 0) + return samples; + return snd_pcm_plugin_src_samples_to_size(snd_pcm_plug_first(handle, SND_PCM_CHANNEL_PLAYBACK), samples); +} + +ssize_t snd_pcm_plug_read_transfer(snd_pcm_plugin_handle_t *handle, snd_pcm_plugin_voice_t *dst_voices_final, size_t size) +{ + snd_pcm_plugin_t *plugin, *next; + snd_pcm_plugin_voice_t *src_voices, *dst_voices; + ssize_t samples; + int err; + + plugin = snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE); + samples = snd_pcm_plugin_dst_size_to_samples(plugin, size); + if (samples < 0) + return samples; + samples = snd_pcm_plug_slave_samples(handle, SND_PCM_CHANNEL_CAPTURE, samples); + if (samples < 0) + return samples; + + plugin = snd_pcm_plug_first(handle, SND_PCM_CHANNEL_CAPTURE); + if ((err = snd_pcm_plugin_slave_voices(plugin, samples, &src_voices)) < 0) + return err; + if ((err = snd_pcm_plug_capture_disable_useless_voices(handle, src_voices, dst_voices_final) < 0)) + return err; + + while (plugin && samples > 0) { + if ((next = plugin->next) != NULL) { + if ((err = plugin->client_voices(plugin, samples, &dst_voices)) < 0) { + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); + return err; + } + } else { + dst_voices = dst_voices_final; + } + pdprintf("read plugin: %s, %i\n", plugin->name, samples); + if ((samples = plugin->transfer(plugin, src_voices, dst_voices, samples)) < 0) { + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, dst_voices->aptr); + return samples; + } +#if 0 + { + unsigned int voice; + for (voice = 0; voice < plugin->src_format.voices; ++voice) { + fprintf(stderr, "%d%d ", src_voices[voice].enabled, src_voices[voice].wanted); + } + fprintf(stderr, " -> "); + for (voice = 0; voice < plugin->dst_format.voices; ++voice) { + fprintf(stderr, "%d%d ", dst_voices[voice].enabled, dst_voices[voice].wanted); + } + fprintf(stderr, "\n"); + } +#endif + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); + plugin = next; + src_voices = dst_voices; + } + snd_pcm_plug_buf_unlock(handle, SND_PCM_CHANNEL_CAPTURE, src_voices->aptr); + return snd_pcm_plugin_dst_samples_to_size(snd_pcm_plug_last(handle, SND_PCM_CHANNEL_CAPTURE), samples); +} diff --git a/src/pcm/plugin/Makefile.am b/src/pcm/plugin/Makefile.am index 0f0eb9aa..bdbada97 100644 --- a/src/pcm/plugin/Makefile.am +++ b/src/pcm/plugin/Makefile.am @@ -1,6 +1,6 @@ EXTRA_LTLIBRARIES = libpcmplugin.la -libpcmplugin_la_SOURCES = block.c mmap.c stream.c linear.c \ +libpcmplugin_la_SOURCES = block.c mmap.c stream.c copy.c linear.c \ mulaw.c alaw.c adpcm.c rate.c route.c all: libpcmplugin.la diff --git a/src/pcm/plugin/adpcm.c b/src/pcm/plugin/adpcm.c index 4b55873b..f85e42a1 100644 --- a/src/pcm/plugin/adpcm.c +++ b/src/pcm/plugin/adpcm.c @@ -78,7 +78,7 @@ typedef struct { typedef void (*adpcm_f)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples); typedef struct adpcm_private_data { @@ -90,7 +90,7 @@ typedef struct adpcm_private_data { static void adpcm_init(snd_pcm_plugin_t *plugin) { - int voice; + unsigned int voice; adpcm_t *data = (adpcm_t *)plugin->extra_data; for (voice = 0; voice < plugin->src_format.voices; voice++) { adpcm_voice_t *v = &data->voices[voice]; @@ -212,7 +212,7 @@ static int adpcm_decoder(unsigned char code, adpcm_voice_t * state) static void adpcm_decode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define PUT16_LABELS @@ -229,13 +229,13 @@ static void adpcm_decode(snd_pcm_plugin_t *plugin, int src_step, srcbit_step, dst_step; size_t samples1; adpcm_voice_t *state; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; srcbit = src_voices[voice].first % 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; @@ -270,7 +270,7 @@ static void adpcm_decode(snd_pcm_plugin_t *plugin, static void adpcm_encode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define GET16_LABELS @@ -288,13 +288,13 @@ static void adpcm_encode(snd_pcm_plugin_t *plugin, int src_step, dst_step, dstbit_step; size_t samples1; adpcm_voice_t *state; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; dstbit = dst_voices[voice].first % 8; @@ -328,22 +328,17 @@ static void adpcm_encode(snd_pcm_plugin_t *plugin, 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, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { adpcm_t *data; - int voice; + unsigned int voice; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].addr != NULL && - dst_voices[voice].addr == NULL) - return -EFAULT; if (plugin->src_format.format == SND_PCM_SFMT_IMA_ADPCM) { if (src_voices[voice].first % 4 != 0 || src_voices[voice].step % 4 != 0 || @@ -365,7 +360,7 @@ static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin, static int adpcm_action(snd_pcm_plugin_t * plugin, snd_pcm_plugin_action_t action, - unsigned long udata) + unsigned long udata UNUSED) { if (plugin == NULL) return -EINVAL; @@ -376,15 +371,19 @@ static int adpcm_action(snd_pcm_plugin_t * plugin, case FLUSH: adpcm_init(plugin); break; + default: + break; } return 0; /* silenty ignore other actions */ } int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { + int err; struct adpcm_private_data *data; snd_pcm_plugin_t *plugin; snd_pcm_format_t *format; @@ -412,13 +411,14 @@ int snd_pcm_plugin_build_adpcm(snd_pcm_plugin_handle_t *handle, if (!snd_pcm_format_linear(format->format)) return -EINVAL; - plugin = snd_pcm_plugin_build(handle, - "Ima-ADPCM<->linear conversion", - src_format, - dst_format, - sizeof(adpcm_t) + src_format->voices * sizeof(adpcm_voice_t)); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(handle, channel, + "Ima-ADPCM<->linear conversion", + src_format, + dst_format, + sizeof(adpcm_t) + src_format->voices * sizeof(adpcm_voice_t), + &plugin); + if (err < 0) + return err; data = (adpcm_t *)plugin->extra_data; data->func = func; data->conv = getput_index(format->format); diff --git a/src/pcm/plugin/alaw.c b/src/pcm/plugin/alaw.c index 8d6b4e42..fce035bd 100644 --- a/src/pcm/plugin/alaw.c +++ b/src/pcm/plugin/alaw.c @@ -134,7 +134,7 @@ static int alaw2linear(unsigned char a_val) typedef void (*alaw_f)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples); typedef struct alaw_private_data { @@ -144,7 +144,7 @@ typedef struct alaw_private_data { static void alaw_decode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define PUT16_LABELS @@ -159,13 +159,13 @@ static void alaw_decode(snd_pcm_plugin_t *plugin, char *dst; int src_step, dst_step; size_t samples1; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -186,7 +186,7 @@ static void alaw_decode(snd_pcm_plugin_t *plugin, static void alaw_encode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define GET16_LABELS @@ -202,13 +202,13 @@ static void alaw_encode(snd_pcm_plugin_t *plugin, char *dst; int src_step, dst_step; size_t samples1; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -229,22 +229,17 @@ static void alaw_encode(snd_pcm_plugin_t *plugin, static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { alaw_t *data; - int voice; + unsigned int voice; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].addr != NULL && - dst_voices[voice].addr == NULL) - return -EFAULT; if (src_voices[voice].first % 8 != 0 || src_voices[voice].step % 8 != 0) return -EINVAL; @@ -258,10 +253,12 @@ static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin, } int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { + int err; alaw_t *data; snd_pcm_plugin_t *plugin; snd_pcm_format_t *format; @@ -289,13 +286,14 @@ int snd_pcm_plugin_build_alaw(snd_pcm_plugin_handle_t *handle, if (!snd_pcm_format_linear(format->format)) return -EINVAL; - plugin = snd_pcm_plugin_build(handle, - "A-Law<->linear conversion", - src_format, - dst_format, - sizeof(alaw_t)); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(handle, channel, + "A-Law<->linear conversion", + src_format, + dst_format, + sizeof(alaw_t), + &plugin); + if (err < 0) + return err; data = (alaw_t*)plugin->extra_data; data->func = func; data->conv = getput_index(format->format); diff --git a/src/pcm/plugin/block.c b/src/pcm/plugin/block.c index e3753f5b..7c83e967 100644 --- a/src/pcm/plugin/block.c +++ b/src/pcm/plugin/block.c @@ -19,6 +19,15 @@ * */ +#ifdef __KERNEL__ +#include "../../include/driver.h" +#include "../../include/pcm.h" +#include "../../include/pcm_plugin.h" +#define snd_pcm_write(handle,buf,count) snd_pcm_oss_write3(handle,buf,count,1) +#define snd_pcm_writev(handle,vec,count) snd_pcm_oss_writev3(handle,vec,count,1) +#define snd_pcm_read(handle,buf,count) snd_pcm_oss_read3(handle,buf,count,1) +#define snd_pcm_readv(handle,vec,count) snd_pcm_oss_readv3(handle,vec,count,1) +#else #include #include #include @@ -26,18 +35,19 @@ #include #include #include "../pcm_local.h" +#endif /* * Basic block plugin */ typedef struct block_private_data { - int channel; + snd_pcm_plugin_handle_t *slave; } block_t; static ssize_t block_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { block_t *data; @@ -51,40 +61,50 @@ static ssize_t block_transfer(snd_pcm_plugin_t *plugin, if (data == NULL) return -EINVAL; vec = (struct iovec *)((char *)data + sizeof(*data)); - if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { + if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) { if (src_voices == NULL) return -EINVAL; if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0) return result; + count = plugin->src_format.voices; if (plugin->src_format.interleave) { - result = snd_pcm_write(plugin->handle, src_voices->addr, result); + result = snd_pcm_write(data->slave, src_voices->addr, result); } else { - count = plugin->src_format.voices; result /= count; for (voice = 0; voice < count; voice++) { - vec[voice].iov_base = src_voices[voice].addr; + if (src_voices[voice].enabled) + vec[voice].iov_base = src_voices[voice].addr; + else + vec[voice].iov_base = 0; vec[voice].iov_len = result; } - result = snd_pcm_writev(plugin->handle, vec, count); + result = snd_pcm_writev(data->slave, 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) { + } else if (plugin->channel == SND_PCM_CHANNEL_CAPTURE) { if (dst_voices == NULL) return -EINVAL; if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0) return result; + count = plugin->dst_format.voices; if (plugin->dst_format.interleave) { - result = snd_pcm_read(plugin->handle, dst_voices->addr, result); + result = snd_pcm_read(data->slave, dst_voices->addr, result); + for (voice = 0; voice < count; voice++) { + dst_voices[voice].enabled = src_voices[voice].enabled; + } } else { - count = plugin->dst_format.voices; result /= count; for (voice = 0; voice < count; voice++) { - vec[voice].iov_base = dst_voices[voice].addr; + dst_voices[voice].enabled = src_voices[voice].enabled; + if (dst_voices[voice].enabled) + vec[voice].iov_base = dst_voices[voice].addr; + else + vec[voice].iov_base = 0; vec[voice].iov_len = result; } - result = snd_pcm_readv(plugin->handle, vec, count); + result = snd_pcm_readv(data->slave, vec, count); } if (result < 0) return result; @@ -94,29 +114,49 @@ static ssize_t block_transfer(snd_pcm_plugin_t *plugin, } } -int snd_pcm_plugin_build_block(snd_pcm_t *pcm, int channel, +static int block_src_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) +{ + int err; + unsigned int voice; + snd_pcm_plugin_voice_t *v; + err = snd_pcm_plugin_client_voices(plugin, samples, &v); + if (err < 0) + return err; + *voices = v; + for (voice = 0; voice < plugin->src_format.voices; ++voice, ++v) + v->wanted = 1; + return 0; +} + +int snd_pcm_plugin_build_block(snd_pcm_plugin_handle_t *pcm, + int channel, + snd_pcm_plugin_handle_t *slave, snd_pcm_format_t *format, snd_pcm_plugin_t **r_plugin) { + int err; block_t *data; snd_pcm_plugin_t *plugin; if (r_plugin == NULL) return -EINVAL; *r_plugin = NULL; - if (pcm == NULL || channel < 0 || channel > 1 || format == NULL) + if (pcm == NULL || format == NULL) return -EINVAL; - plugin = snd_pcm_plugin_build(pcm, - channel == SND_PCM_CHANNEL_PLAYBACK ? - "I/O block playback" : - "I/O block capture", - format, format, - sizeof(block_t) + sizeof(struct iovec) * format->voices); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(pcm, channel, + "I/O block", + format, format, + sizeof(block_t) + sizeof(struct iovec) * format->voices, + &plugin); + if (err < 0) + return err; data = (block_t *)plugin->extra_data; - data->channel = channel; + data->slave = slave; plugin->transfer = block_transfer; + if (format->interleave && channel == SND_PCM_CHANNEL_PLAYBACK) + plugin->client_voices = block_src_voices; *r_plugin = plugin; return 0; } diff --git a/src/pcm/plugin/copy.c b/src/pcm/plugin/copy.c new file mode 100644 index 00000000..88a61d75 --- /dev/null +++ b/src/pcm/plugin/copy.c @@ -0,0 +1,156 @@ +/* + * Linear conversion Plug-In + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ +#include "../../include/driver.h" +#include "../../include/pcm.h" +#include "../../include/pcm_plugin.h" +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pcm_local.h" +#endif + +typedef struct copy_private_data { + int copy; +} copy_t; + +static void copy(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + snd_pcm_plugin_voice_t *dst_voices, + size_t samples) +{ +#define COPY_LABELS +#include "plugin_ops.h" +#undef COPY_LABELS + copy_t *data = (copy_t *)plugin->extra_data; + void *copy = copy_labels[data->copy]; + int voice; + int nvoices = plugin->src_format.voices; + for (voice = 0; voice < nvoices; ++voice) { + char *src; + char *dst; + int src_step, dst_step; + size_t samples1; + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; + continue; + } + dst_voices[voice].enabled = 1; + src = src_voices[voice].addr + src_voices[voice].first / 8; + dst = dst_voices[voice].addr + dst_voices[voice].first / 8; + src_step = src_voices[voice].step / 8; + dst_step = dst_voices[voice].step / 8; + samples1 = samples; + while (samples1-- > 0) { + goto *copy; +#define COPY_END after +#include "plugin_ops.h" +#undef COPY_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static ssize_t copy_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + snd_pcm_plugin_voice_t *dst_voices, + size_t samples) +{ + copy_t *data; + unsigned int voice; + + if (plugin == NULL || src_voices == NULL || dst_voices == NULL) + return -EFAULT; + data = (copy_t *)plugin->extra_data; + if (samples == 0) + return 0; + for (voice = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].first % 8 != 0 || + src_voices[voice].step % 8 != 0) + return -EINVAL; + if (dst_voices[voice].first % 8 != 0 || + dst_voices[voice].step % 8 != 0) + return -EINVAL; + } + copy(plugin, src_voices, dst_voices, samples); + return samples; +} + +int copy_index(int format) +{ + int size = snd_pcm_format_physical_width(format); + switch (size) { + case 8: + return 0; + case 16: + return 1; + case 32: + return 2; + case 64: + return 3; + default: + return -EINVAL; + } +} + +int snd_pcm_plugin_build_copy(snd_pcm_plugin_handle_t *handle, + int channel, + snd_pcm_format_t *format, + snd_pcm_plugin_t **r_plugin) +{ + int err; + struct copy_private_data *data; + snd_pcm_plugin_t *plugin; + int copy; + + if (r_plugin == NULL) + return -EFAULT; + *r_plugin = NULL; + + copy = copy_index(format->format); + if (copy < 0) + return -EINVAL; + + err = snd_pcm_plugin_build(handle, channel, + "copy", + format, + format, + sizeof(copy_t), + &plugin); + if (err < 0) + return err; + data = (copy_t *)plugin->extra_data; + data->copy = copy; + plugin->transfer = copy_transfer; + *r_plugin = plugin; + return 0; +} diff --git a/src/pcm/plugin/linear.c b/src/pcm/plugin/linear.c index ed499b15..2ba444bf 100644 --- a/src/pcm/plugin/linear.c +++ b/src/pcm/plugin/linear.c @@ -41,19 +41,19 @@ */ typedef struct linear_private_data { - int copy; + int conv; } linear_t; static void convert(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { -#define COPY_LABELS +#define CONV_LABELS #include "plugin_ops.h" -#undef COPY_LABELS +#undef CONV_LABELS linear_t *data = (linear_t *)plugin->extra_data; - void *copy = copy_labels[data->copy]; + void *conv = conv_labels[data->conv]; int voice; int nvoices = plugin->src_format.voices; for (voice = 0; voice < nvoices; ++voice) { @@ -61,23 +61,23 @@ static void convert(snd_pcm_plugin_t *plugin, char *dst; int src_step, dst_step; size_t samples1; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; dst_step = dst_voices[voice].step / 8; samples1 = samples; while (samples1-- > 0) { - goto *copy; -#define COPY_END after + goto *conv; +#define CONV_END after #include "plugin_ops.h" -#undef COPY_END +#undef CONV_END after: src += src_step; dst += dst_step; @@ -87,23 +87,18 @@ static void convert(snd_pcm_plugin_t *plugin, static ssize_t linear_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { linear_t *data; - int voice; + unsigned int voice; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; data = (linear_t *)plugin->extra_data; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].addr != NULL && - dst_voices[voice].addr == NULL) - return -EFAULT; if (src_voices[voice].first % 8 != 0 || src_voices[voice].step % 8 != 0) return -EINVAL; @@ -115,7 +110,7 @@ static ssize_t linear_transfer(snd_pcm_plugin_t *plugin, return samples; } -int copy_index(int src_format, int dst_format) +int conv_index(int src_format, int dst_format) { int src_endian, dst_endian, sign, src_width, dst_width; @@ -143,10 +138,12 @@ int copy_index(int src_format, int dst_format) } int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { + int err; struct linear_private_data *data; snd_pcm_plugin_t *plugin; @@ -162,15 +159,16 @@ int snd_pcm_plugin_build_linear(snd_pcm_plugin_handle_t *handle, snd_pcm_format_linear(dst_format->format))) return -EINVAL; - plugin = snd_pcm_plugin_build(handle, - "linear format conversion", - src_format, - dst_format, - sizeof(linear_t)); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(handle, channel, + "linear format conversion", + src_format, + dst_format, + sizeof(linear_t), + &plugin); + if (err < 0) + return err; data = (linear_t *)plugin->extra_data; - data->copy = copy_index(src_format->format, dst_format->format); + data->conv = conv_index(src_format->format, dst_format->format); plugin->transfer = linear_transfer; *r_plugin = plugin; return 0; diff --git a/src/pcm/plugin/mmap.c b/src/pcm/plugin/mmap.c index 421c530b..c1751729 100644 --- a/src/pcm/plugin/mmap.c +++ b/src/pcm/plugin/mmap.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "../pcm_local.h" @@ -33,310 +34,248 @@ */ typedef struct mmap_private_data { - int channel; + snd_pcm_t *slave; snd_pcm_mmap_control_t *control; - char *buffer; - int frag; - int frag_size, samples_frag_size; - int start_mode, stop_mode; - int frags, frags_used; - int frags_min, frags_max; - unsigned int lastblock; + void *buffer; + unsigned int frag; + size_t samples_frag_size; + char *silence; snd_pcm_plugin_voice_t voices[0]; } mmap_t; -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; - - if (delta < data->lastblock) { - delta += (~0 - data->lastblock) + 1; - } else { - delta -= data->lastblock; - } - data->frags_used -= delta; - if (data->frags_used < 0) { - /* correction for rollover */ - data->frag += -data->frags_used; - data->frag %= data->frags; - data->frags_used = 0; - } - data->lastblock += delta; - return data->frags_used <= data->frags_max && - (data->frags - data->frags_used) >= data->frags_min; -} -static int poll_playback(snd_pcm_t *pcm) +static int mmap_src_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) { - int err; + mmap_t *data; + unsigned int voice; + snd_pcm_plugin_voice_t *dv, *sv; + struct snd_pcm_chan *chan; + snd_pcm_channel_setup_t *setup; + snd_pcm_mmap_control_t *ctrl; + int frag, f; struct pollfd pfd; - - if (pcm->mode & SND_PCM_OPEN_NONBLOCK) - return -EAGAIN; - pfd.fd = pcm->chan[SND_PCM_CHANNEL_PLAYBACK].fd; - pfd.events = POLLOUT; - pfd.revents = 0; - err = poll(&pfd, 1, 1000); - return err < 0 ? err : 0; -} + int ready; -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; + if (plugin == NULL || voices == NULL) + return -EINVAL; + data = (mmap_t *)plugin->extra_data; + if (samples != data->samples_frag_size) + return -EINVAL; - switch (control->status.status) { - case SND_PCM_STATUS_PREPARED: - if (data->start_mode == SND_PCM_START_GO) + ctrl = data->control; + chan = &plugin->handle->chan[plugin->channel]; + setup = &chan->setup; + if (ctrl->status < SND_PCM_STATUS_PREPARED) + return -EBADFD; + + ready = snd_pcm_mmap_ready(data->slave, plugin->channel); + if (ready < 0) + return ready; + if (!ready) { + if (ctrl->status != SND_PCM_STATUS_RUNNING) + return -EPIPE; + if (chan->mode & SND_PCM_NONBLOCK) return -EAGAIN; - if ((data->start_mode == SND_PCM_START_DATA && - playback_ok(plugin)) || - (data->start_mode == SND_PCM_START_FULL && - data->frags_used == data->frags)) { - 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 + 1; - err = poll_playback(plugin->handle); - if (err < 0) - return err; - } - break; - case SND_PCM_STATUS_UNDERRUN: - return -EPIPE; - default: - return -EIO; + pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel); + pfd.events = POLLOUT | POLLERR; + ready = poll(&pfd, 1, 10000); + if (ready < 0) + return ready; + if (ready && pfd.revents & POLLERR) + return -EPIPE; + assert(snd_pcm_mmap_ready(data->slave, plugin->channel)); } - return 0; -} + frag = ctrl->frag_data; + f = frag % setup->frags; -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; - - if (delta < data->lastblock) { - delta += (~0 - data->lastblock) + 1; - } else { - delta -= data->lastblock; - } - data->frags_used += delta; - if (data->frags_used > data->frags) { - /* correction for rollover */ - data->frag += data->frags_used - data->frags; - data->frag %= data->frags; - data->frags_used = data->frags; + dv = data->voices; + sv = plugin->src_voices; + *voices = sv; + for (voice = 0; voice < plugin->src_format.voices; ++voice) { + sv->enabled = 1; + sv->wanted = !data->silence[voice * setup->frags + f]; + sv->aptr = 0; + sv->addr = dv->addr + (dv->step * data->samples_frag_size * f) / 8; + sv->first = dv->first; + sv->step = dv->step; + ++sv; + ++dv; } - data->lastblock += delta; - return data->frags_used >= data->frags_min; + data->frag = frag; + return 0; } -static int poll_capture(snd_pcm_t *pcm) +static int mmap_dst_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) { + mmap_t *data; int err; + unsigned int voice; + snd_pcm_plugin_voice_t *dv, *sv; + struct snd_pcm_chan *chan; + snd_pcm_channel_setup_t *setup; + snd_pcm_mmap_control_t *ctrl; + int frag, f; struct pollfd pfd; + int ready; - if (pcm->mode & SND_PCM_OPEN_NONBLOCK) - return -EAGAIN; - pfd.fd = pcm->chan[SND_PCM_CHANNEL_CAPTURE].fd; - pfd.events = POLLIN; - pfd.revents = 0; - err = poll(&pfd, 1, 1000); - return err < 0 ? err : 0; -} - -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; + if (plugin == NULL || voices == NULL) + return -EINVAL; + data = (mmap_t *)plugin->extra_data; + if (samples != data->samples_frag_size) + return -EINVAL; - switch (control->status.status) { - case SND_PCM_STATUS_PREPARED: - if (data->start_mode != SND_PCM_START_DATA) - return -EAGAIN; - err = snd_pcm_channel_go(plugin->handle, data->channel); + chan = &plugin->handle->chan[plugin->channel]; + setup = &chan->setup; + ctrl = data->control; + if (ctrl->status < SND_PCM_STATUS_PREPARED) + return -EBADFD; + if (ctrl->status == SND_PCM_STATUS_PREPARED && + chan->setup.start_mode == SND_PCM_START_DATA) { + err = snd_pcm_channel_go(data->slave, plugin->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(plugin->handle); + } + ready = snd_pcm_mmap_ready(data->slave, plugin->channel); + if (ready < 0) + return ready; + if (!ready) { + if (ctrl->status == SND_PCM_STATUS_PREPARED && + chan->setup.start_mode == SND_PCM_START_FULL) { + err = snd_pcm_channel_go(data->slave, plugin->channel); if (err < 0) return err; } - break; - case SND_PCM_STATUS_OVERRUN: - return -EPIPE; - default: - return -EIO; + if (ctrl->status != SND_PCM_STATUS_RUNNING) + return -EPIPE; + if (chan->mode & SND_PCM_NONBLOCK) + return -EAGAIN; + pfd.fd = snd_pcm_file_descriptor(plugin->handle, plugin->channel); + pfd.events = POLLIN | POLLERR; + ready = poll(&pfd, 1, 10000); + if (ready < 0) + return ready; + if (ready && pfd.revents & POLLERR) + return -EPIPE; + assert(snd_pcm_mmap_ready(data->slave, plugin->channel)); } - return 0; -} -static int mmap_src_voices(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_voice_t **voices, - size_t samples) -{ - mmap_t *data; - int err; - int voice; - snd_pcm_plugin_voice_t *dv, *sv; + frag = ctrl->frag_data; + f = frag % setup->frags; - if (plugin == NULL || voices == NULL) - return -EINVAL; - data = (mmap_t *)plugin->extra_data; - if (data->channel != SND_PCM_CHANNEL_PLAYBACK) - return -EINVAL; - if (snd_pcm_plugin_dst_samples_to_size(plugin, samples) != data->frag_size) - return -EINVAL; - /* wait until the block is not free */ - while (!playback_ok(plugin)) { - err = query_playback(plugin, 0); - if (err < 0) - return err; - } sv = data->voices; - dv = plugin->voices; - for (voice = 0; voice < plugin->src_format.voices; ++voice) { - dv->addr = sv->addr + (sv->step * data->samples_frag_size * data->frag) / 8; + dv = plugin->dst_voices; + *voices = dv; + for (voice = 0; voice < plugin->dst_format.voices; ++voice) { + dv->enabled = 1; + dv->wanted = 0; + dv->aptr = 0; + dv->addr = sv->addr + (sv->step * data->samples_frag_size * f) / 8; dv->first = sv->first; dv->step = sv->step; ++sv; ++dv; } - *voices = plugin->voices; + data->frag = frag; return 0; } -static int mmap_dst_voices(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_voice_t **voices, - size_t samples) +static ssize_t mmap_playback_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices, + snd_pcm_plugin_voice_t *dst_voices UNUSED, + size_t samples) { mmap_t *data; - int voice; - snd_pcm_plugin_voice_t *dv, *sv; + unsigned int voice; + snd_pcm_channel_setup_t *setup; + snd_pcm_mmap_control_t *ctrl; + struct snd_pcm_chan *chan; + unsigned int frag, f; + int err; - if (plugin == NULL || voices == NULL) + if (plugin == NULL) return -EINVAL; data = (mmap_t *)plugin->extra_data; - if (data->channel != SND_PCM_CHANNEL_CAPTURE) + if (src_voices == NULL) return -EINVAL; - if (snd_pcm_plugin_src_samples_to_size(plugin, samples) != data->frag_size) + if (plugin->prev == NULL) return -EINVAL; - sv = data->voices; - dv = plugin->voices; - for (voice = 0; voice < plugin->src_format.voices; ++voice) { - dv->addr = sv->addr + (sv->step * data->samples_frag_size * data->frag) / 8; - dv->first = sv->first; - dv->step = sv->step; - ++sv; - ++dv; + ctrl = data->control; + if (ctrl == NULL) + return -EINVAL; + chan = &data->slave->chan[SND_PCM_CHANNEL_PLAYBACK]; + setup = &chan->setup; + frag = ctrl->frag_data; + if (frag != data->frag) + return -EIO; + f = frag % setup->frags; + + for (voice = 0; voice < plugin->src_format.voices; voice++) { + if (src_voices[voice].enabled) + data->silence[voice * setup->frags + f] = 0; } - *voices = plugin->voices; - return 0; -} -static ssize_t mmap_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) + frag++; + if (frag == setup->frag_boundary) { + ctrl->frag_data = 0; + ctrl->pos_data = 0; + } else { + ctrl->frag_data = frag; + ctrl->pos_data += setup->frag_size; + } + if (ctrl->status == SND_PCM_STATUS_PREPARED && + (chan->setup.start_mode == SND_PCM_START_DATA || + (chan->setup.start_mode == SND_PCM_START_FULL && + !snd_pcm_mmap_ready(data->slave, plugin->channel)))) { + err = snd_pcm_channel_go(data->slave, plugin->channel); + if (err < 0) + return err; + } + return samples; +} + +static ssize_t mmap_capture_transfer(snd_pcm_plugin_t *plugin, + const snd_pcm_plugin_voice_t *src_voices UNUSED, + snd_pcm_plugin_voice_t *dst_voices UNUSED, + size_t samples) { mmap_t *data; - snd_pcm_mmap_control_t *control; - ssize_t size; - int err; + snd_pcm_channel_setup_t *setup; + snd_pcm_mmap_control_t *ctrl; + unsigned int frag; if (plugin == NULL) return -EINVAL; data = (mmap_t *)plugin->extra_data; - control = data->control; - if (control == NULL) + if (plugin->next == NULL) return -EINVAL; - if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { - if (src_voices == NULL) - return -EINVAL; - while (!playback_ok(plugin)) { - err = query_playback(plugin, 0); - if (err < 0) - return err; - } - size = snd_pcm_plugin_src_samples_to_size(plugin, samples); - if (size != data->frag_size) - return -EINVAL; - if (plugin->prev == NULL) { - if (plugin->src_format.interleave) { - void *dst = data->voices[0].addr + data->frag * data->frag_size; - /* Paranoia: add check for src_voices */ - memcpy(dst, src_voices[0].addr, size); - } else { - int voice; - size /= plugin->src_format.voices; - for (voice = 0; voice < plugin->src_format.voices; ++voice) { - void *dst = data->voices[voice].addr + (data->voices[voice].step * data->samples_frag_size * data->frag) / 8; - /* Paranoia: add check for src_voices */ - memcpy(dst, src_voices[voice].addr, size); - } - } - } - control->fragments[data->frag].data = 1; - data->frag++; - data->frag %= data->frags; - data->frags_used++; - return samples; - } else if (data->channel == SND_PCM_CHANNEL_CAPTURE) { - if (dst_voices == NULL) - return -EINVAL; - while (!capture_ok(plugin)) { - err = query_capture(plugin, 0); - if (err < 0) - return err; - } - size = snd_pcm_plugin_dst_samples_to_size(plugin, samples); - if (size != data->frag_size) - return -EINVAL; - if (plugin->next == NULL) { - if (plugin->dst_format.interleave) { - void *src = data->voices[0].addr + data->frag * data->frag_size; - /* Paranoia: add check for dst_voices */ - memcpy(dst_voices[0].addr, src, size); - } else { - int voice; - size /= plugin->src_format.voices; - for (voice = 0; voice < plugin->src_format.voices; ++voice) { - void *src = data->voices[voice].addr + (data->voices[voice].step * data->samples_frag_size * data->frag) / 8; - /* Paranoia: add check for dst_voices */ - memcpy(dst_voices[voice].addr, src, size); - } - } - control->fragments[data->frag].data = 0; - } else { - int prev_frag = data->frag - 1; - if (prev_frag < 0) - prev_frag = data->frags - 1; - control->fragments[prev_frag].data = 0; - } - data->frag++; - data->frag %= data->frags; - data->frags_used--; - return samples; - } else { + + ctrl = data->control; + if (ctrl == NULL) return -EINVAL; + frag = ctrl->frag_data; + if (frag != data->frag) + return -EIO; + setup = &data->slave->chan[SND_PCM_CHANNEL_CAPTURE].setup; + + /* FIXME: not here the increment */ + frag++; + if (frag == setup->frag_boundary) { + ctrl->frag_data = 0; + ctrl->pos_data = 0; + } else { + ctrl->frag_data = frag; + ctrl->pos_data += setup->frag_size; } + return samples; } static int mmap_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action, - unsigned long udata) + unsigned long udata UNUSED) { struct mmap_private_data *data; @@ -344,48 +283,25 @@ static int mmap_action(snd_pcm_plugin_t *plugin, return -EINVAL; data = (mmap_t *)plugin->extra_data; if (action == INIT) { - snd_pcm_channel_params_t *params; - snd_pcm_channel_setup_t setup; + snd_pcm_channel_setup_t *setup; int result; - int voice; + unsigned int voice; snd_pcm_plugin_voice_t *v; if (data->control) - snd_pcm_munmap(plugin->handle, data->channel); - result = snd_pcm_mmap(plugin->handle, data->channel, &data->control, (void **)&data->buffer); + snd_pcm_munmap(data->slave, plugin->channel); + result = snd_pcm_mmap(data->slave, plugin->channel, &data->control, (void **)&data->buffer); if (result < 0) return result; - params = (snd_pcm_channel_params_t *)udata; - data->start_mode = params->start_mode; - data->stop_mode = params->stop_mode; - memset(&setup, 0, sizeof(setup)); - setup.channel = data->channel; - if ((result = snd_pcm_channel_setup(plugin->handle, &setup)) < 0) - return result; - data->frags = setup.buf.block.frags; - data->frag_size = setup.buf.block.frag_size; - data->samples_frag_size = data->frag_size / snd_pcm_format_size(plugin->src_format.format, plugin->src_format.voices); - data->frags_min = setup.buf.block.frags_min; - data->frags_max = setup.buf.block.frags_max; - if (data->frags_min < 0) - data->frags_min = 0; - if (data->frags_min >= setup.buf.block.frags) - data->frags_min = setup.buf.block.frags - 1; - if (data->frags_max < 0) - data->frags_max = setup.buf.block.frags + data->frags_max; - if (data->frags_max < data->frags_min) - data->frags_max = data->frags_min; - if (data->frags_max < 1) - data->frags_max = 1; - if (data->frags_max > setup.buf.block.frags) - data->frags_max = setup.buf.block.frags; + setup = &data->slave->chan[plugin->channel].setup; + data->samples_frag_size = setup->frag_size / snd_pcm_format_size(setup->format.format, setup->format.voices); v = data->voices; - for (voice = 0; voice < plugin->src_format.voices; ++voice) { + for (voice = 0; voice < setup->format.voices; ++voice) { snd_pcm_voice_setup_t vsetup; vsetup.voice = voice; - if ((result = snd_pcm_voice_setup(plugin->handle, data->channel, &vsetup)) < 0) + if ((result = snd_pcm_voice_setup(data->slave, plugin->channel, &vsetup)) < 0) return result; if (vsetup.addr < 0) return -EBADFD; @@ -394,56 +310,60 @@ static int mmap_action(snd_pcm_plugin_t *plugin, v->step = vsetup.step; v++; } + if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) { + data->silence = malloc(setup->frags * setup->format.voices); + memset(data->silence, 0, setup->frags * setup->format.voices); + } else + data->silence = 0; return 0; - } else if (action == PREPARE) { - data->frag = 0; - data->lastblock = 0; - } else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) { - data->frag = 0; - data->lastblock = 0; - } else if (action == FLUSH) { - data->frag = 0; - data->lastblock = 0; } return 0; /* silenty ignore other actions */ } -static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data) +static void mmap_free(snd_pcm_plugin_t *plugin, void *private_data UNUSED) { struct mmap_private_data *data; if (plugin == NULL) return; data = (mmap_t *)plugin->extra_data; + if (data->silence) + free(data->silence); if (data->control) - snd_pcm_munmap(plugin->handle, data->channel); + snd_pcm_munmap(data->slave, plugin->channel); } -int snd_pcm_plugin_build_mmap(snd_pcm_t *pcm, int channel, +int snd_pcm_plugin_build_mmap(snd_pcm_plugin_handle_t *pcm, + int channel, + snd_pcm_t *slave, snd_pcm_format_t *format, snd_pcm_plugin_t **r_plugin) { + int err; mmap_t *data; snd_pcm_plugin_t *plugin; if (r_plugin == NULL) return -EINVAL; *r_plugin = NULL; - if (!pcm || channel < 0 || channel > 1) + if (!pcm) return -EINVAL; - plugin = snd_pcm_plugin_build(pcm, - channel == SND_PCM_CHANNEL_PLAYBACK ? - "I/O mmap playback" : - "I/O mmap capture", - format, format, - sizeof(mmap_t) + sizeof(snd_pcm_plugin_voice_t) * format->voices); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(pcm, channel, + "I/O mmap", + format, format, + sizeof(mmap_t) + sizeof(snd_pcm_plugin_voice_t) * format->voices, + &plugin); + if (err < 0) + return err; data = (mmap_t *)plugin->extra_data; - data->channel = channel; - plugin->src_voices = mmap_src_voices; - plugin->dst_voices = mmap_dst_voices; - plugin->transfer = mmap_transfer; + data->slave = slave; + if (channel == SND_PCM_CHANNEL_PLAYBACK) { + plugin->client_voices = mmap_src_voices; + plugin->transfer = mmap_playback_transfer; + } else { + plugin->client_voices = mmap_dst_voices; + plugin->transfer = mmap_capture_transfer; + } plugin->action = mmap_action; plugin->private_free = mmap_free; *r_plugin = plugin; diff --git a/src/pcm/plugin/mulaw.c b/src/pcm/plugin/mulaw.c index a76a3ab9..4e8964ab 100644 --- a/src/pcm/plugin/mulaw.c +++ b/src/pcm/plugin/mulaw.c @@ -150,7 +150,7 @@ static int ulaw2linear(unsigned char u_val) typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples); typedef struct mulaw_private_data { @@ -160,7 +160,7 @@ typedef struct mulaw_private_data { static void mulaw_decode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define PUT16_LABELS @@ -175,13 +175,13 @@ static void mulaw_decode(snd_pcm_plugin_t *plugin, char *dst; int src_step, dst_step; size_t samples1; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -202,7 +202,7 @@ static void mulaw_decode(snd_pcm_plugin_t *plugin, static void mulaw_encode(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { #define GET16_LABELS @@ -218,13 +218,13 @@ static void mulaw_encode(snd_pcm_plugin_t *plugin, char *dst; int src_step, dst_step; size_t samples1; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = src_voices[voice].addr + src_voices[voice].first / 8; dst = dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -245,22 +245,17 @@ static void mulaw_encode(snd_pcm_plugin_t *plugin, static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { mulaw_t *data; - int voice; + unsigned int voice; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].addr != NULL && - dst_voices[voice].addr == NULL) - return -EFAULT; if (src_voices[voice].first % 8 != 0 || src_voices[voice].step % 8 != 0) return -EINVAL; @@ -274,10 +269,12 @@ static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin, } int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { + int err; mulaw_t *data; snd_pcm_plugin_t *plugin; snd_pcm_format_t *format; @@ -305,13 +302,14 @@ int snd_pcm_plugin_build_mulaw(snd_pcm_plugin_handle_t *handle, if (!snd_pcm_format_linear(format->format)) return -EINVAL; - plugin = snd_pcm_plugin_build(handle, - "Mu-Law<->linear conversion", - src_format, - dst_format, - sizeof(mulaw_t)); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(handle, channel, + "Mu-Law<->linear conversion", + src_format, + dst_format, + sizeof(mulaw_t), + &plugin); + if (err < 0) + return err; data = (mulaw_t*)plugin->extra_data; data->func = func; data->conv = getput_index(format->format); diff --git a/src/pcm/plugin/plugin_ops.h b/src/pcm/plugin/plugin_ops.h index 28c7743c..4217339c 100644 --- a/src/pcm/plugin/plugin_ops.h +++ b/src/pcm/plugin/plugin_ops.h @@ -23,241 +23,260 @@ #define as_u8(ptr) (*(u_int8_t*)(ptr)) #define as_u16(ptr) (*(u_int16_t*)(ptr)) #define as_u32(ptr) (*(u_int32_t*)(ptr)) +#define as_u64(ptr) (*(u_int64_t*)(ptr)) #define as_s8(ptr) (*(int8_t*)(ptr)) #define as_s16(ptr) (*(int16_t*)(ptr)) #define as_s32(ptr) (*(int32_t*)(ptr)) - +#define as_s64(ptr) (*(int64_t*)(ptr)) #ifdef COPY_LABELS -/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ -static void *copy_labels[4 * 2 * 2 * 4 * 2] = { - &©_xxx1_xxx1, /* 8h -> 8h */ - &©_xxx1_xxx1, /* 8h -> 8s */ - &©_xxx1_xx10, /* 8h -> 16h */ - &©_xxx1_xx01, /* 8h -> 16s */ - &©_xxx1_x100, /* 8h -> 24h */ - &©_xxx1_001x, /* 8h -> 24s */ - &©_xxx1_1000, /* 8h -> 32h */ - &©_xxx1_0001, /* 8h -> 32s */ - &©_xxx1_xxx9, /* 8h ^> 8h */ - &©_xxx1_xxx9, /* 8h ^> 8s */ - &©_xxx1_xx90, /* 8h ^> 16h */ - &©_xxx1_xx09, /* 8h ^> 16s */ - &©_xxx1_x900, /* 8h ^> 24h */ - &©_xxx1_009x, /* 8h ^> 24s */ - &©_xxx1_9000, /* 8h ^> 32h */ - &©_xxx1_0009, /* 8h ^> 32s */ - &©_xxx1_xxx1, /* 8s -> 8h */ - &©_xxx1_xxx1, /* 8s -> 8s */ - &©_xxx1_xx10, /* 8s -> 16h */ - &©_xxx1_xx01, /* 8s -> 16s */ - &©_xxx1_x100, /* 8s -> 24h */ - &©_xxx1_001x, /* 8s -> 24s */ - &©_xxx1_1000, /* 8s -> 32h */ - &©_xxx1_0001, /* 8s -> 32s */ - &©_xxx1_xxx9, /* 8s ^> 8h */ - &©_xxx1_xxx9, /* 8s ^> 8s */ - &©_xxx1_xx90, /* 8s ^> 16h */ - &©_xxx1_xx09, /* 8s ^> 16s */ - &©_xxx1_x900, /* 8s ^> 24h */ - &©_xxx1_009x, /* 8s ^> 24s */ - &©_xxx1_9000, /* 8s ^> 32h */ - &©_xxx1_0009, /* 8s ^> 32s */ - &©_xx12_xxx1, /* 16h -> 8h */ - &©_xx12_xxx1, /* 16h -> 8s */ - &©_xx12_xx12, /* 16h -> 16h */ - &©_xx12_xx21, /* 16h -> 16s */ - &©_xx12_x120, /* 16h -> 24h */ - &©_xx12_021x, /* 16h -> 24s */ - &©_xx12_1200, /* 16h -> 32h */ - &©_xx12_0021, /* 16h -> 32s */ - &©_xx12_xxx9, /* 16h ^> 8h */ - &©_xx12_xxx9, /* 16h ^> 8s */ - &©_xx12_xx92, /* 16h ^> 16h */ - &©_xx12_xx29, /* 16h ^> 16s */ - &©_xx12_x920, /* 16h ^> 24h */ - &©_xx12_029x, /* 16h ^> 24s */ - &©_xx12_9200, /* 16h ^> 32h */ - &©_xx12_0029, /* 16h ^> 32s */ - &©_xx12_xxx2, /* 16s -> 8h */ - &©_xx12_xxx2, /* 16s -> 8s */ - &©_xx12_xx21, /* 16s -> 16h */ - &©_xx12_xx12, /* 16s -> 16s */ - &©_xx12_x210, /* 16s -> 24h */ - &©_xx12_012x, /* 16s -> 24s */ - &©_xx12_2100, /* 16s -> 32h */ - &©_xx12_0012, /* 16s -> 32s */ - &©_xx12_xxxA, /* 16s ^> 8h */ - &©_xx12_xxxA, /* 16s ^> 8s */ - &©_xx12_xxA1, /* 16s ^> 16h */ - &©_xx12_xx1A, /* 16s ^> 16s */ - &©_xx12_xA10, /* 16s ^> 24h */ - &©_xx12_01Ax, /* 16s ^> 24s */ - &©_xx12_A100, /* 16s ^> 32h */ - &©_xx12_001A, /* 16s ^> 32s */ - &©_x123_xxx1, /* 24h -> 8h */ - &©_x123_xxx1, /* 24h -> 8s */ - &©_x123_xx12, /* 24h -> 16h */ - &©_x123_xx21, /* 24h -> 16s */ - &©_x123_x123, /* 24h -> 24h */ - &©_x123_321x, /* 24h -> 24s */ - &©_x123_1230, /* 24h -> 32h */ - &©_x123_0321, /* 24h -> 32s */ - &©_x123_xxx9, /* 24h ^> 8h */ - &©_x123_xxx9, /* 24h ^> 8s */ - &©_x123_xx92, /* 24h ^> 16h */ - &©_x123_xx29, /* 24h ^> 16s */ - &©_x123_x923, /* 24h ^> 24h */ - &©_x123_329x, /* 24h ^> 24s */ - &©_x123_9230, /* 24h ^> 32h */ - &©_x123_0329, /* 24h ^> 32s */ - &©_123x_xxx3, /* 24s -> 8h */ - &©_123x_xxx3, /* 24s -> 8s */ - &©_123x_xx32, /* 24s -> 16h */ - &©_123x_xx23, /* 24s -> 16s */ - &©_123x_x321, /* 24s -> 24h */ - &©_123x_123x, /* 24s -> 24s */ - &©_123x_3210, /* 24s -> 32h */ - &©_123x_0123, /* 24s -> 32s */ - &©_123x_xxxB, /* 24s ^> 8h */ - &©_123x_xxxB, /* 24s ^> 8s */ - &©_123x_xxB2, /* 24s ^> 16h */ - &©_123x_xx2B, /* 24s ^> 16s */ - &©_123x_xB21, /* 24s ^> 24h */ - &©_123x_12Bx, /* 24s ^> 24s */ - &©_123x_B210, /* 24s ^> 32h */ - &©_123x_012B, /* 24s ^> 32s */ - &©_1234_xxx1, /* 32h -> 8h */ - &©_1234_xxx1, /* 32h -> 8s */ - &©_1234_xx12, /* 32h -> 16h */ - &©_1234_xx21, /* 32h -> 16s */ - &©_1234_x123, /* 32h -> 24h */ - &©_1234_321x, /* 32h -> 24s */ - &©_1234_1234, /* 32h -> 32h */ - &©_1234_4321, /* 32h -> 32s */ - &©_1234_xxx9, /* 32h ^> 8h */ - &©_1234_xxx9, /* 32h ^> 8s */ - &©_1234_xx92, /* 32h ^> 16h */ - &©_1234_xx29, /* 32h ^> 16s */ - &©_1234_x923, /* 32h ^> 24h */ - &©_1234_329x, /* 32h ^> 24s */ - &©_1234_9234, /* 32h ^> 32h */ - &©_1234_4329, /* 32h ^> 32s */ - &©_1234_xxx4, /* 32s -> 8h */ - &©_1234_xxx4, /* 32s -> 8s */ - &©_1234_xx43, /* 32s -> 16h */ - &©_1234_xx34, /* 32s -> 16s */ - &©_1234_x432, /* 32s -> 24h */ - &©_1234_234x, /* 32s -> 24s */ - &©_1234_4321, /* 32s -> 32h */ - &©_1234_1234, /* 32s -> 32s */ - &©_1234_xxxC, /* 32s ^> 8h */ - &©_1234_xxxC, /* 32s ^> 8s */ - &©_1234_xxC3, /* 32s ^> 16h */ - &©_1234_xx3C, /* 32s ^> 16s */ - &©_1234_xC32, /* 32s ^> 24h */ - &©_1234_23Cx, /* 32s ^> 24s */ - &©_1234_C321, /* 32s ^> 32h */ - &©_1234_123C, /* 32s ^> 32s */ +static void *copy_labels[4] = { + &©_8, + &©_16, + &©_32, + &©_64 }; #endif #ifdef COPY_END while(0) { -copy_xxx1_xxx1: as_u8(dst) = as_u8(src); goto COPY_END; -copy_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto COPY_END; -copy_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto COPY_END; -copy_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto COPY_END; -copy_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto COPY_END; -copy_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto COPY_END; -copy_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto COPY_END; -copy_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto COPY_END; -copy_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto COPY_END; -copy_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto COPY_END; -copy_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto COPY_END; -copy_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto COPY_END; -copy_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto COPY_END; -copy_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto COPY_END; -copy_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto COPY_END; -copy_xx12_xx12: as_u16(dst) = as_u16(src); goto COPY_END; -copy_xx12_xx21: as_u16(dst) = bswap_16(as_u16(src)); goto COPY_END; -copy_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto COPY_END; -copy_xx12_021x: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto COPY_END; -copy_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto COPY_END; -copy_xx12_0021: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)); goto COPY_END; -copy_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto COPY_END; -copy_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto COPY_END; -copy_xx12_xx29: as_u16(dst) = bswap_16(as_u16(src)) ^ 0x80; goto COPY_END; -copy_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto COPY_END; -copy_xx12_029x: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80) << 8; goto COPY_END; -copy_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto COPY_END; -copy_xx12_0029: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80); goto COPY_END; -copy_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto COPY_END; -copy_xx12_x210: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto COPY_END; -copy_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto COPY_END; -copy_xx12_2100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 16; goto COPY_END; -copy_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto COPY_END; -copy_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto COPY_END; -copy_xx12_xxA1: as_u16(dst) = bswap_16(as_u16(src) ^ 0x80); goto COPY_END; -copy_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto COPY_END; -copy_xx12_xA10: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 8; goto COPY_END; -copy_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto COPY_END; -copy_xx12_A100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 16; goto COPY_END; -copy_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto COPY_END; -copy_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto COPY_END; -copy_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto COPY_END; -copy_x123_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto COPY_END; -copy_x123_x123: as_u32(dst) = as_u32(src); goto COPY_END; -copy_x123_321x: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END; -copy_x123_1230: as_u32(dst) = as_u32(src) << 8; goto COPY_END; -copy_x123_0321: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto COPY_END; -copy_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto COPY_END; -copy_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto COPY_END; -copy_x123_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 8) ^ 0x80; goto COPY_END; -copy_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto COPY_END; -copy_x123_329x: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x8000; goto COPY_END; -copy_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto COPY_END; -copy_x123_0329: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x80; goto COPY_END; -copy_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto COPY_END; -copy_123x_xx32: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto COPY_END; -copy_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto COPY_END; -copy_123x_x321: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END; -copy_123x_123x: as_u32(dst) = as_u32(src); goto COPY_END; -copy_123x_3210: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto COPY_END; -copy_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto COPY_END; -copy_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto COPY_END; -copy_123x_xxB2: as_u16(dst) = bswap_16((as_u32(src) >> 8) ^ 0x80); goto COPY_END; -copy_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto COPY_END; -copy_123x_xB21: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x800000; goto COPY_END; -copy_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto COPY_END; -copy_123x_B210: as_u32(dst) = bswap_32(as_u32(src) ^ 0x8000) << 8; goto COPY_END; -copy_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto COPY_END; -copy_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto COPY_END; -copy_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto COPY_END; -copy_1234_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 16); goto COPY_END; -copy_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto COPY_END; -copy_1234_321x: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto COPY_END; -copy_1234_1234: as_u32(dst) = as_u32(src); goto COPY_END; -copy_1234_4321: as_u32(dst) = bswap_32(as_u32(src)); goto COPY_END; -copy_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto COPY_END; -copy_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto COPY_END; -copy_1234_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 16) ^ 0x80; goto COPY_END; -copy_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto COPY_END; -copy_1234_329x: as_u32(dst) = (bswap_32(as_u32(src)) ^ 0x80) << 8; goto COPY_END; -copy_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto COPY_END; -copy_1234_4329: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x80; goto COPY_END; -copy_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto COPY_END; -copy_1234_xx43: as_u16(dst) = bswap_16(as_u32(src)); goto COPY_END; -copy_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto COPY_END; -copy_1234_x432: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto COPY_END; -copy_1234_234x: as_u32(dst) = as_u32(src) << 8; goto COPY_END; -copy_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto COPY_END; -copy_1234_xxC3: as_u16(dst) = bswap_16(as_u32(src) ^ 0x80); goto COPY_END; -copy_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto COPY_END; -copy_1234_xC32: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x800000; goto COPY_END; -copy_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto COPY_END; -copy_1234_C321: as_u32(dst) = bswap_32(as_u32(src) ^ 0x80); goto COPY_END; -copy_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto COPY_END; +copy_8: as_s8(dst) = as_s8(src); goto COPY_END; +copy_16: as_s16(dst) = as_s16(src); goto COPY_END; +copy_32: as_s32(dst) = as_s32(src); goto COPY_END; +copy_64: as_s64(dst) = as_s64(src); goto COPY_END; +} +#endif + +#ifdef CONV_LABELS +/* src_wid src_endswap sign_toggle dst_wid dst_endswap */ +static void *conv_labels[4 * 2 * 2 * 4 * 2] = { + &&conv_xxx1_xxx1, /* 8h -> 8h */ + &&conv_xxx1_xxx1, /* 8h -> 8s */ + &&conv_xxx1_xx10, /* 8h -> 16h */ + &&conv_xxx1_xx01, /* 8h -> 16s */ + &&conv_xxx1_x100, /* 8h -> 24h */ + &&conv_xxx1_001x, /* 8h -> 24s */ + &&conv_xxx1_1000, /* 8h -> 32h */ + &&conv_xxx1_0001, /* 8h -> 32s */ + &&conv_xxx1_xxx9, /* 8h ^> 8h */ + &&conv_xxx1_xxx9, /* 8h ^> 8s */ + &&conv_xxx1_xx90, /* 8h ^> 16h */ + &&conv_xxx1_xx09, /* 8h ^> 16s */ + &&conv_xxx1_x900, /* 8h ^> 24h */ + &&conv_xxx1_009x, /* 8h ^> 24s */ + &&conv_xxx1_9000, /* 8h ^> 32h */ + &&conv_xxx1_0009, /* 8h ^> 32s */ + &&conv_xxx1_xxx1, /* 8s -> 8h */ + &&conv_xxx1_xxx1, /* 8s -> 8s */ + &&conv_xxx1_xx10, /* 8s -> 16h */ + &&conv_xxx1_xx01, /* 8s -> 16s */ + &&conv_xxx1_x100, /* 8s -> 24h */ + &&conv_xxx1_001x, /* 8s -> 24s */ + &&conv_xxx1_1000, /* 8s -> 32h */ + &&conv_xxx1_0001, /* 8s -> 32s */ + &&conv_xxx1_xxx9, /* 8s ^> 8h */ + &&conv_xxx1_xxx9, /* 8s ^> 8s */ + &&conv_xxx1_xx90, /* 8s ^> 16h */ + &&conv_xxx1_xx09, /* 8s ^> 16s */ + &&conv_xxx1_x900, /* 8s ^> 24h */ + &&conv_xxx1_009x, /* 8s ^> 24s */ + &&conv_xxx1_9000, /* 8s ^> 32h */ + &&conv_xxx1_0009, /* 8s ^> 32s */ + &&conv_xx12_xxx1, /* 16h -> 8h */ + &&conv_xx12_xxx1, /* 16h -> 8s */ + &&conv_xx12_xx12, /* 16h -> 16h */ + &&conv_xx12_xx21, /* 16h -> 16s */ + &&conv_xx12_x120, /* 16h -> 24h */ + &&conv_xx12_021x, /* 16h -> 24s */ + &&conv_xx12_1200, /* 16h -> 32h */ + &&conv_xx12_0021, /* 16h -> 32s */ + &&conv_xx12_xxx9, /* 16h ^> 8h */ + &&conv_xx12_xxx9, /* 16h ^> 8s */ + &&conv_xx12_xx92, /* 16h ^> 16h */ + &&conv_xx12_xx29, /* 16h ^> 16s */ + &&conv_xx12_x920, /* 16h ^> 24h */ + &&conv_xx12_029x, /* 16h ^> 24s */ + &&conv_xx12_9200, /* 16h ^> 32h */ + &&conv_xx12_0029, /* 16h ^> 32s */ + &&conv_xx12_xxx2, /* 16s -> 8h */ + &&conv_xx12_xxx2, /* 16s -> 8s */ + &&conv_xx12_xx21, /* 16s -> 16h */ + &&conv_xx12_xx12, /* 16s -> 16s */ + &&conv_xx12_x210, /* 16s -> 24h */ + &&conv_xx12_012x, /* 16s -> 24s */ + &&conv_xx12_2100, /* 16s -> 32h */ + &&conv_xx12_0012, /* 16s -> 32s */ + &&conv_xx12_xxxA, /* 16s ^> 8h */ + &&conv_xx12_xxxA, /* 16s ^> 8s */ + &&conv_xx12_xxA1, /* 16s ^> 16h */ + &&conv_xx12_xx1A, /* 16s ^> 16s */ + &&conv_xx12_xA10, /* 16s ^> 24h */ + &&conv_xx12_01Ax, /* 16s ^> 24s */ + &&conv_xx12_A100, /* 16s ^> 32h */ + &&conv_xx12_001A, /* 16s ^> 32s */ + &&conv_x123_xxx1, /* 24h -> 8h */ + &&conv_x123_xxx1, /* 24h -> 8s */ + &&conv_x123_xx12, /* 24h -> 16h */ + &&conv_x123_xx21, /* 24h -> 16s */ + &&conv_x123_x123, /* 24h -> 24h */ + &&conv_x123_321x, /* 24h -> 24s */ + &&conv_x123_1230, /* 24h -> 32h */ + &&conv_x123_0321, /* 24h -> 32s */ + &&conv_x123_xxx9, /* 24h ^> 8h */ + &&conv_x123_xxx9, /* 24h ^> 8s */ + &&conv_x123_xx92, /* 24h ^> 16h */ + &&conv_x123_xx29, /* 24h ^> 16s */ + &&conv_x123_x923, /* 24h ^> 24h */ + &&conv_x123_329x, /* 24h ^> 24s */ + &&conv_x123_9230, /* 24h ^> 32h */ + &&conv_x123_0329, /* 24h ^> 32s */ + &&conv_123x_xxx3, /* 24s -> 8h */ + &&conv_123x_xxx3, /* 24s -> 8s */ + &&conv_123x_xx32, /* 24s -> 16h */ + &&conv_123x_xx23, /* 24s -> 16s */ + &&conv_123x_x321, /* 24s -> 24h */ + &&conv_123x_123x, /* 24s -> 24s */ + &&conv_123x_3210, /* 24s -> 32h */ + &&conv_123x_0123, /* 24s -> 32s */ + &&conv_123x_xxxB, /* 24s ^> 8h */ + &&conv_123x_xxxB, /* 24s ^> 8s */ + &&conv_123x_xxB2, /* 24s ^> 16h */ + &&conv_123x_xx2B, /* 24s ^> 16s */ + &&conv_123x_xB21, /* 24s ^> 24h */ + &&conv_123x_12Bx, /* 24s ^> 24s */ + &&conv_123x_B210, /* 24s ^> 32h */ + &&conv_123x_012B, /* 24s ^> 32s */ + &&conv_1234_xxx1, /* 32h -> 8h */ + &&conv_1234_xxx1, /* 32h -> 8s */ + &&conv_1234_xx12, /* 32h -> 16h */ + &&conv_1234_xx21, /* 32h -> 16s */ + &&conv_1234_x123, /* 32h -> 24h */ + &&conv_1234_321x, /* 32h -> 24s */ + &&conv_1234_1234, /* 32h -> 32h */ + &&conv_1234_4321, /* 32h -> 32s */ + &&conv_1234_xxx9, /* 32h ^> 8h */ + &&conv_1234_xxx9, /* 32h ^> 8s */ + &&conv_1234_xx92, /* 32h ^> 16h */ + &&conv_1234_xx29, /* 32h ^> 16s */ + &&conv_1234_x923, /* 32h ^> 24h */ + &&conv_1234_329x, /* 32h ^> 24s */ + &&conv_1234_9234, /* 32h ^> 32h */ + &&conv_1234_4329, /* 32h ^> 32s */ + &&conv_1234_xxx4, /* 32s -> 8h */ + &&conv_1234_xxx4, /* 32s -> 8s */ + &&conv_1234_xx43, /* 32s -> 16h */ + &&conv_1234_xx34, /* 32s -> 16s */ + &&conv_1234_x432, /* 32s -> 24h */ + &&conv_1234_234x, /* 32s -> 24s */ + &&conv_1234_4321, /* 32s -> 32h */ + &&conv_1234_1234, /* 32s -> 32s */ + &&conv_1234_xxxC, /* 32s ^> 8h */ + &&conv_1234_xxxC, /* 32s ^> 8s */ + &&conv_1234_xxC3, /* 32s ^> 16h */ + &&conv_1234_xx3C, /* 32s ^> 16s */ + &&conv_1234_xC32, /* 32s ^> 24h */ + &&conv_1234_23Cx, /* 32s ^> 24s */ + &&conv_1234_C321, /* 32s ^> 32h */ + &&conv_1234_123C, /* 32s ^> 32s */ +}; +#endif + +#ifdef CONV_END +while(0) { +conv_xxx1_xxx1: as_u8(dst) = as_u8(src); goto CONV_END; +conv_xxx1_xx10: as_u16(dst) = (u_int16_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_xx01: as_u16(dst) = (u_int16_t)as_u8(src); goto CONV_END; +conv_xxx1_x100: as_u32(dst) = (u_int32_t)as_u8(src) << 16; goto CONV_END; +conv_xxx1_001x: as_u32(dst) = (u_int32_t)as_u8(src) << 8; goto CONV_END; +conv_xxx1_1000: as_u32(dst) = (u_int32_t)as_u8(src) << 24; goto CONV_END; +conv_xxx1_0001: as_u32(dst) = (u_int32_t)as_u8(src); goto CONV_END; +conv_xxx1_xxx9: as_u8(dst) = as_u8(src) ^ 0x80; goto CONV_END; +conv_xxx1_xx90: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_xx09: as_u16(dst) = (u_int16_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xxx1_x900: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 16; goto CONV_END; +conv_xxx1_009x: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 8; goto CONV_END; +conv_xxx1_9000: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80) << 24; goto CONV_END; +conv_xxx1_0009: as_u32(dst) = (u_int32_t)(as_u8(src) ^ 0x80); goto CONV_END; +conv_xx12_xxx1: as_u8(dst) = as_u16(src) >> 8; goto CONV_END; +conv_xx12_xx12: as_u16(dst) = as_u16(src); goto CONV_END; +conv_xx12_xx21: as_u16(dst) = bswap_16(as_u16(src)); goto CONV_END; +conv_xx12_x120: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_021x: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_1200: as_u32(dst) = (u_int32_t)as_u16(src) << 16; goto CONV_END; +conv_xx12_0021: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)); goto CONV_END; +conv_xx12_xxx9: as_u8(dst) = (as_u16(src) >> 8) ^ 0x80; goto CONV_END; +conv_xx12_xx92: as_u16(dst) = as_u16(src) ^ 0x8000; goto CONV_END; +conv_xx12_xx29: as_u16(dst) = bswap_16(as_u16(src)) ^ 0x80; goto CONV_END; +conv_xx12_x920: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 8; goto CONV_END; +conv_xx12_029x: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80) << 8; goto CONV_END; +conv_xx12_9200: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x8000) << 16; goto CONV_END; +conv_xx12_0029: as_u32(dst) = (u_int32_t)(bswap_16(as_u16(src)) ^ 0x80); goto CONV_END; +conv_xx12_xxx2: as_u8(dst) = as_u16(src) & 0xff; goto CONV_END; +conv_xx12_x210: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 8; goto CONV_END; +conv_xx12_012x: as_u32(dst) = (u_int32_t)as_u16(src) << 8; goto CONV_END; +conv_xx12_2100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src)) << 16; goto CONV_END; +conv_xx12_0012: as_u32(dst) = (u_int32_t)as_u16(src); goto CONV_END; +conv_xx12_xxxA: as_u8(dst) = (as_u16(src) ^ 0x80) & 0xff; goto CONV_END; +conv_xx12_xxA1: as_u16(dst) = bswap_16(as_u16(src) ^ 0x80); goto CONV_END; +conv_xx12_xx1A: as_u16(dst) = as_u16(src) ^ 0x80; goto CONV_END; +conv_xx12_xA10: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_01Ax: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80) << 8; goto CONV_END; +conv_xx12_A100: as_u32(dst) = (u_int32_t)bswap_16(as_u16(src) ^ 0x80) << 16; goto CONV_END; +conv_xx12_001A: as_u32(dst) = (u_int32_t)(as_u16(src) ^ 0x80); goto CONV_END; +conv_x123_xxx1: as_u8(dst) = as_u32(src) >> 16; goto CONV_END; +conv_x123_xx12: as_u16(dst) = as_u32(src) >> 8; goto CONV_END; +conv_x123_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto CONV_END; +conv_x123_x123: as_u32(dst) = as_u32(src); goto CONV_END; +conv_x123_321x: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END; +conv_x123_1230: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_x123_0321: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto CONV_END; +conv_x123_xxx9: as_u8(dst) = (as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_x123_xx92: as_u16(dst) = (as_u32(src) >> 8) ^ 0x8000; goto CONV_END; +conv_x123_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_x123_x923: as_u32(dst) = as_u32(src) ^ 0x800000; goto CONV_END; +conv_x123_329x: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x8000; goto CONV_END; +conv_x123_9230: as_u32(dst) = (as_u32(src) ^ 0x800000) << 8; goto CONV_END; +conv_x123_0329: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x80; goto CONV_END; +conv_123x_xxx3: as_u8(dst) = (as_u32(src) >> 8) & 0xff; goto CONV_END; +conv_123x_xx32: as_u16(dst) = bswap_16(as_u32(src) >> 8); goto CONV_END; +conv_123x_xx23: as_u16(dst) = (as_u32(src) >> 8) & 0xffff; goto CONV_END; +conv_123x_x321: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END; +conv_123x_123x: as_u32(dst) = as_u32(src); goto CONV_END; +conv_123x_3210: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto CONV_END; +conv_123x_0123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_123x_xxxB: as_u8(dst) = ((as_u32(src) >> 8) & 0xff) ^ 0x80; goto CONV_END; +conv_123x_xxB2: as_u16(dst) = bswap_16((as_u32(src) >> 8) ^ 0x80); goto CONV_END; +conv_123x_xx2B: as_u16(dst) = ((as_u32(src) >> 8) & 0xffff) ^ 0x80; goto CONV_END; +conv_123x_xB21: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x800000; goto CONV_END; +conv_123x_12Bx: as_u32(dst) = as_u32(src) ^ 0x8000; goto CONV_END; +conv_123x_B210: as_u32(dst) = bswap_32(as_u32(src) ^ 0x8000) << 8; goto CONV_END; +conv_123x_012B: as_u32(dst) = (as_u32(src) >> 8) ^ 0x80; goto CONV_END; +conv_1234_xxx1: as_u8(dst) = as_u32(src) >> 24; goto CONV_END; +conv_1234_xx12: as_u16(dst) = as_u32(src) >> 16; goto CONV_END; +conv_1234_xx21: as_u16(dst) = bswap_16(as_u32(src) >> 16); goto CONV_END; +conv_1234_x123: as_u32(dst) = as_u32(src) >> 8; goto CONV_END; +conv_1234_321x: as_u32(dst) = bswap_32(as_u32(src)) << 8; goto CONV_END; +conv_1234_1234: as_u32(dst) = as_u32(src); goto CONV_END; +conv_1234_4321: as_u32(dst) = bswap_32(as_u32(src)); goto CONV_END; +conv_1234_xxx9: as_u8(dst) = (as_u32(src) >> 24) ^ 0x80; goto CONV_END; +conv_1234_xx92: as_u16(dst) = (as_u32(src) >> 16) ^ 0x8000; goto CONV_END; +conv_1234_xx29: as_u16(dst) = bswap_16(as_u32(src) >> 16) ^ 0x80; goto CONV_END; +conv_1234_x923: as_u32(dst) = (as_u32(src) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_329x: as_u32(dst) = (bswap_32(as_u32(src)) ^ 0x80) << 8; goto CONV_END; +conv_1234_9234: as_u32(dst) = as_u32(src) ^ 0x80000000; goto CONV_END; +conv_1234_4329: as_u32(dst) = bswap_32(as_u32(src)) ^ 0x80; goto CONV_END; +conv_1234_xxx4: as_u8(dst) = as_u32(src) & 0xff; goto CONV_END; +conv_1234_xx43: as_u16(dst) = bswap_16(as_u32(src)); goto CONV_END; +conv_1234_xx34: as_u16(dst) = as_u32(src) & 0xffff; goto CONV_END; +conv_1234_x432: as_u32(dst) = bswap_32(as_u32(src)) >> 8; goto CONV_END; +conv_1234_234x: as_u32(dst) = as_u32(src) << 8; goto CONV_END; +conv_1234_xxxC: as_u8(dst) = (as_u32(src) & 0xff) ^ 0x80; goto CONV_END; +conv_1234_xxC3: as_u16(dst) = bswap_16(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_xx3C: as_u16(dst) = (as_u32(src) & 0xffff) ^ 0x80; goto CONV_END; +conv_1234_xC32: as_u32(dst) = (bswap_32(as_u32(src)) >> 8) ^ 0x800000; goto CONV_END; +conv_1234_23Cx: as_u32(dst) = (as_u32(src) ^ 0x80) << 8; goto CONV_END; +conv_1234_C321: as_u32(dst) = bswap_32(as_u32(src) ^ 0x80); goto CONV_END; +conv_1234_123C: as_u32(dst) = as_u32(src) ^ 0x80; goto CONV_END; } #endif diff --git a/src/pcm/plugin/rate.c b/src/pcm/plugin/rate.c index 7bba217c..3afc0272 100644 --- a/src/pcm/plugin/rate.c +++ b/src/pcm/plugin/rate.c @@ -49,7 +49,7 @@ typedef struct { typedef void (*rate_f)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, int src_samples, int dst_samples); typedef struct rate_private_data { @@ -63,7 +63,7 @@ typedef struct rate_private_data { static void rate_init(snd_pcm_plugin_t *plugin) { - int voice; + unsigned int voice; rate_t *data = (rate_t *)plugin->extra_data; data->pos = 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { @@ -74,14 +74,14 @@ static void rate_init(snd_pcm_plugin_t *plugin) 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, + snd_pcm_plugin_voice_t *dst_voices, int src_samples, int dst_samples) { unsigned int pos = 0; signed int val; signed short S1, S2; char *src, *dst; - int voice; + unsigned int voice; int src_step, dst_step; int src_samples1, dst_samples1; rate_t *data = (rate_t *)plugin->extra_data; @@ -104,13 +104,13 @@ static void resample_expand(snd_pcm_plugin_t *plugin, pos = data->pos; S1 = rvoices->last_S1; S2 = rvoices->last_S2; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], dst_samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = (char *)src_voices[voice].addr + src_voices[voice].first / 8; dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -162,14 +162,14 @@ static void resample_expand(snd_pcm_plugin_t *plugin, 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, + snd_pcm_plugin_voice_t *dst_voices, int src_samples, int dst_samples) { unsigned int pos = 0; signed int val; signed short S1, S2; char *src, *dst; - int voice; + unsigned int voice; int src_step, dst_step; int src_samples1, dst_samples1; rate_t *data = (rate_t *)plugin->extra_data; @@ -188,13 +188,13 @@ static void resample_shrink(snd_pcm_plugin_t *plugin, pos = data->pos; S1 = rvoices->last_S1; S2 = rvoices->last_S2; - if (src_voices[voice].addr == NULL) { - if (dst_voices[voice].addr != NULL) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, &dst_voices[voice], dst_samples); - } + if (!src_voices[voice].enabled) { + if (dst_voices[voice].wanted) + snd_pcm_plugin_silence_voice(plugin, &dst_voices[voice], dst_samples); + dst_voices[voice].enabled = 0; continue; } + dst_voices[voice].enabled = 1; src = (char *)src_voices[voice].addr + src_voices[voice].first / 8; dst = (char *)dst_voices[voice].addr + dst_voices[voice].first / 8; src_step = src_voices[voice].step / 8; @@ -301,23 +301,18 @@ static ssize_t rate_dst_samples(snd_pcm_plugin_t *plugin, size_t samples) static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { size_t dst_samples; - int voice; + unsigned int voice; rate_t *data; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; for (voice = 0; voice < plugin->src_format.voices; voice++) { - if (src_voices[voice].addr != NULL && - dst_voices[voice].addr == NULL) - return -EFAULT; if (src_voices[voice].first % 8 != 0 || src_voices[voice].step % 8 != 0) return -EINVAL; @@ -334,7 +329,7 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, static int rate_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action, - unsigned long udata) + unsigned long udata UNUSED) { if (plugin == NULL) return -EINVAL; @@ -345,15 +340,19 @@ static int rate_action(snd_pcm_plugin_t *plugin, case FLUSH: rate_init(plugin); break; + default: + break; } return 0; /* silenty ignore other actions */ } int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, snd_pcm_plugin_t **r_plugin) { + int err; rate_t *data; snd_pcm_plugin_t *plugin; @@ -372,13 +371,14 @@ int snd_pcm_plugin_build_rate(snd_pcm_plugin_handle_t *handle, if (src_format->rate == dst_format->rate) return -EINVAL; - 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; + err = snd_pcm_plugin_build(handle, channel, + "rate conversion", + src_format, + dst_format, + sizeof(rate_t) + src_format->voices * sizeof(rate_voice_t), + &plugin); + if (err < 0) + return err; data = (rate_t *)plugin->extra_data; data->get = getput_index(src_format->format); data->put = getput_index(dst_format->format); diff --git a/src/pcm/plugin/route.c b/src/pcm/plugin/route.c index 293cbcb9..ba99d8ab 100644 --- a/src/pcm/plugin/route.c +++ b/src/pcm/plugin/route.c @@ -1,6 +1,6 @@ /* * Attenuated route Plug-In - * Copyright (c) 2000 by Abramo Bagnara + * Copyright (c) 2000 by Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify @@ -23,8 +23,6 @@ #include "../../include/driver.h" #include "../../include/pcm.h" #include "../../include/pcm_plugin.h" -#define my_calloc(size) snd_kmalloc(size, GFP_KERNEL) -#define my_free(ptr) snd_kfree(ptr) #else #include #include @@ -35,8 +33,6 @@ #include #include #include "../pcm_local.h" -#define my_calloc(size) calloc(1, size) -#define my_free(ptr) free(ptr) #endif typedef struct ttable_dst ttable_dst_t; @@ -44,7 +40,7 @@ typedef struct route_private_data route_t; typedef void (*route_voice_f)(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voice, + snd_pcm_plugin_voice_t *dst_voice, ttable_dst_t* ttable, size_t samples); typedef struct { @@ -65,7 +61,7 @@ struct ttable_dst { struct route_private_data { enum {INT32=0, INT64=1, FLOAT=2} sum_type; int get, put; - int copy; + int conv; int src_sample_size; ttable_dst_t ttable[0]; }; @@ -79,77 +75,26 @@ typedef union { } sum_t; -void zero_voice(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_voice_t *dst_voice, - size_t samples) -{ - char *dst = dst_voice->addr + dst_voice->first / 8; - int dst_step = dst_voice->step / 8; - switch (plugin->dst_width) { - case 4: { - int dstbit = dst_voice->first % 8; - int dstbit_step = dst_voice->step % 8; - while (samples-- > 0) { - if (dstbit) - *dst &= 0x0f; - else - *dst &= 0xf0; - dst += dst_step; - dstbit += dstbit_step; - if (dstbit == 8) { - dst++; - dstbit = 0; - } - } - break; - } - case 8: - while (samples-- > 0) { - *dst = 0; - dst += dst_step; - } - break; - case 16: - while (samples-- > 0) { - *(int16_t*)dst = 0; - dst += dst_step; - } - break; - case 32: - while (samples-- > 0) { - *(int32_t*)dst = 0; - dst += dst_step; - } - break; - case 64: - while (samples-- > 0) { - *(int64_t*)dst = 0; - dst += dst_step; - } - break; - } -} - - static void route_to_voice_zero(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voice, - ttable_dst_t* ttable, size_t samples) + const snd_pcm_plugin_voice_t *src_voices UNUSED, + snd_pcm_plugin_voice_t *dst_voice, + ttable_dst_t* ttable UNUSED, size_t samples) { -// null_voice(&dst_voices[voice]); - zero_voice(plugin, dst_voice, samples); + if (dst_voice->wanted) + snd_pcm_plugin_silence_voice(plugin, dst_voice, samples); + dst_voice->enabled = 0; } static void route_to_voice_one(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voice, + snd_pcm_plugin_voice_t *dst_voice, ttable_dst_t* ttable, size_t samples) { -#define COPY_LABELS +#define CONV_LABELS #include "plugin_ops.h" -#undef COPY_LABELS +#undef CONV_LABELS route_t *data = (route_t *)plugin->extra_data; - void *copy; + void *conv; const snd_pcm_plugin_voice_t *src_voice = 0; int srcidx; char *src, *dst; @@ -164,16 +109,17 @@ static void route_to_voice_one(snd_pcm_plugin_t *plugin, return; } - copy = copy_labels[data->copy]; + dst_voice->enabled = 1; + conv = conv_labels[data->conv]; src = src_voice->addr + src_voice->first / 8; src_step = src_voice->step / 8; dst = dst_voice->addr + dst_voice->first / 8; dst_step = dst_voice->step / 8; while (samples-- > 0) { - goto *copy; -#define COPY_END after + goto *conv; +#define CONV_END after #include "plugin_ops.h" -#undef COPY_END +#undef CONV_END after: src += src_step; dst += dst_step; @@ -182,7 +128,7 @@ static void route_to_voice_one(snd_pcm_plugin_t *plugin, static void route_to_voice(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voice, + snd_pcm_plugin_voice_t *dst_voice, ttable_dst_t* ttable, size_t samples) { #define GET_LABELS @@ -242,7 +188,7 @@ static void route_to_voice(snd_pcm_plugin_t *plugin, int srcidx, srcidx1 = 0; for (srcidx = 0; srcidx < nsrcs; ++srcidx) { const snd_pcm_plugin_voice_t *src_voice = &src_voices[ttable->srcs[srcidx].voice]; - if (src_voice->addr == NULL) + if (!src_voice->enabled) continue; srcs[srcidx1] = src_voice->addr + src_voices->first / 8; src_steps[srcidx1] = src_voice->step / 8; @@ -258,6 +204,7 @@ static void route_to_voice(snd_pcm_plugin_t *plugin, return; } + dst_voice->enabled = 1; zero = zero_labels[data->sum_type]; get = get_labels[data->get]; add = add_labels[data->sum_type * 2 + ttable->att]; @@ -416,6 +363,55 @@ static void route_to_voice(snd_pcm_plugin_t *plugin, } } +int route_src_voices_mask(snd_pcm_plugin_t *plugin, + bitset_t *dst_vmask, + bitset_t **src_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int svoices = plugin->src_format.voices; + int dvoices = plugin->dst_format.voices; + bitset_t *vmask = plugin->src_vmask; + int voice; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, svoices); + for (voice = 0; voice < dvoices; voice++, dp++) { + int src; + ttable_src_t *sp; + if (!bitset_get(dst_vmask, voice)) + continue; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) + bitset_set(vmask, sp->voice); + } + *src_vmask = vmask; + return 0; +} + +int route_dst_voices_mask(snd_pcm_plugin_t *plugin, + bitset_t *src_vmask, + bitset_t **dst_vmask) +{ + route_t *data = (route_t *)plugin->extra_data; + int dvoices = plugin->dst_format.voices; + bitset_t *vmask = plugin->dst_vmask; + int voice; + ttable_dst_t *dp = data->ttable; + bitset_zero(vmask, dvoices); + for (voice = 0; voice < dvoices; voice++, dp++) { + int src; + ttable_src_t *sp; + sp = dp->srcs; + for (src = 0; src < dp->nsrcs; src++, sp++) { + if (bitset_get(src_vmask, sp->voice)) { + bitset_set(vmask, voice); + break; + } + } + } + *dst_vmask = vmask; + return 0; +} + #ifdef __KERNEL__ #define FULL ROUTE_PLUGIN_RESOLUTION typedef int src_ttable_entry_t; @@ -424,13 +420,13 @@ typedef int src_ttable_entry_t; typedef float src_ttable_entry_t; #endif -static void route_free(snd_pcm_plugin_t *plugin, void* private_data) +static void route_free(snd_pcm_plugin_t *plugin, void* private_data UNUSED) { route_t *data = (route_t *)plugin->extra_data; - int dst_voice; + unsigned int dst_voice; for (dst_voice = 0; dst_voice < plugin->dst_format.voices; ++dst_voice) { if (data->ttable[dst_voice].srcs != NULL) - my_free(data->ttable[dst_voice].srcs); + free(data->ttable[dst_voice].srcs); } } @@ -438,7 +434,7 @@ static int route_load_ttable(snd_pcm_plugin_t *plugin, const src_ttable_entry_t* src_ttable) { route_t *data; - int src_voice, dst_voice; + unsigned int src_voice, dst_voice; const src_ttable_entry_t *sptr; ttable_dst_t *dptr; if (src_ttable == NULL) @@ -488,8 +484,11 @@ static int route_load_ttable(snd_pcm_plugin_t *plugin, dptr->func = route_to_voice; break; } - dptr->srcs = my_calloc(sizeof(*srcs) * nsrcs); - memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs); + if (nsrcs > 0) { + dptr->srcs = calloc(nsrcs, sizeof(*srcs)); + memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs); + } else + dptr->srcs = 0; dptr++; } return 0; @@ -497,19 +496,17 @@ static int route_load_ttable(snd_pcm_plugin_t *plugin, static ssize_t route_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { route_t *data; int src_nvoices, dst_nvoices; int src_voice, dst_voice; ttable_dst_t *ttp; - const snd_pcm_plugin_voice_t *dvp; + snd_pcm_plugin_voice_t *dvp; if (plugin == NULL || src_voices == NULL || dst_voices == NULL) return -EFAULT; - if (samples < 0) - return -EINVAL; if (samples == 0) return 0; data = (route_t *)plugin->extra_data; @@ -556,6 +553,7 @@ int getput_index(int format) } int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle, + int channel, snd_pcm_format_t *src_format, snd_pcm_format_t *dst_format, src_ttable_entry_t *ttable, @@ -574,19 +572,20 @@ int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle, snd_pcm_format_linear(dst_format->format))) return -EINVAL; - plugin = snd_pcm_plugin_build(handle, - "attenuated route conversion", - src_format, - dst_format, - sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->voices); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(handle, channel, + "attenuated route conversion", + src_format, + dst_format, + sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->voices, + &plugin); + if (err < 0) + return err; data = (route_t *) plugin->extra_data; data->get = getput_index(src_format->format); data->put = getput_index(dst_format->format); - data->copy = copy_index(src_format->format, dst_format->format); + data->conv = conv_index(src_format->format, dst_format->format); #ifdef __KERNEL__ if (snd_pcm_format_width(src_format->format) == 32) @@ -603,6 +602,8 @@ int snd_pcm_plugin_build_route(snd_pcm_plugin_handle_t *handle, return err; } plugin->transfer = route_transfer; + plugin->src_voices_mask = route_src_voices_mask; + plugin->dst_voices_mask = route_dst_voices_mask; *r_plugin = plugin; return 0; } diff --git a/src/pcm/plugin/stream.c b/src/pcm/plugin/stream.c index 6ac38a55..57bfe0c4 100644 --- a/src/pcm/plugin/stream.c +++ b/src/pcm/plugin/stream.c @@ -32,12 +32,12 @@ */ typedef struct stream_private_data { - int channel; + snd_pcm_t *slave; } stream_t; static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, const snd_pcm_plugin_voice_t *src_voices, - const snd_pcm_plugin_voice_t *dst_voices, + snd_pcm_plugin_voice_t *dst_voices, size_t samples) { stream_t *data; @@ -51,41 +51,49 @@ static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, if (data == NULL) return -EINVAL; vec = (struct iovec *)((char *)data + sizeof(*data)); - if (data->channel == SND_PCM_CHANNEL_PLAYBACK) { + if (plugin->channel == SND_PCM_CHANNEL_PLAYBACK) { if (src_voices == NULL) return -EINVAL; if ((result = snd_pcm_plugin_src_samples_to_size(plugin, samples)) < 0) return result; + count = plugin->src_format.voices; if (plugin->src_format.interleave) { - result = snd_pcm_write(plugin->handle, src_voices->addr, result); + result = snd_pcm_write(data->slave, src_voices->addr, result); } else { - count = plugin->src_format.voices; result /= count; for (voice = 0; voice < count; voice++) { - vec[voice].iov_base = src_voices[voice].addr; + if (src_voices[voice].enabled) + vec[voice].iov_base = src_voices[voice].addr; + else + vec[voice].iov_base = 0; vec[voice].iov_len = result; } - result = snd_pcm_writev(plugin->handle, vec, count); + result = snd_pcm_writev(data->slave, 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) { + } else if (plugin->channel == SND_PCM_CHANNEL_CAPTURE) { if (dst_voices == NULL) return -EINVAL; if ((result = snd_pcm_plugin_dst_samples_to_size(plugin, samples)) < 0) return result; + count = plugin->dst_format.voices; if (plugin->dst_format.interleave) { - result = snd_pcm_read(plugin->handle, dst_voices->addr, result); - + result = snd_pcm_read(data->slave, dst_voices->addr, result); + for (voice = 0; voice < count; voice++) + dst_voices[voice].enabled = src_voices[voice].enabled; } else { - count = plugin->dst_format.voices; result /= count; for (voice = 0; voice < count; voice++) { - vec[voice].iov_base = dst_voices[voice].addr; + dst_voices[voice].enabled = src_voices[voice].enabled; + if (dst_voices[voice].enabled) + vec[voice].iov_base = dst_voices[voice].addr; + else + vec[voice].iov_base = 0; vec[voice].iov_len = result; } - result = snd_pcm_readv(plugin->handle, vec, count); + result = snd_pcm_readv(data->slave, vec, count); } if (result < 0) return result; @@ -95,10 +103,29 @@ static ssize_t stream_transfer(snd_pcm_plugin_t *plugin, } } -int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel, +static int stream_src_voices(snd_pcm_plugin_t *plugin, + size_t samples, + snd_pcm_plugin_voice_t **voices) +{ + int err; + unsigned int voice; + snd_pcm_plugin_voice_t *v; + err = snd_pcm_plugin_client_voices(plugin, samples, &v); + if (err < 0) + return err; + *voices = v; + for (voice = 0; voice < plugin->src_format.voices; ++voice, ++v) + v->wanted = 1; + return 0; +} + +int snd_pcm_plugin_build_stream(snd_pcm_plugin_handle_t *pcm, + int channel, + snd_pcm_t *slave, snd_pcm_format_t *format, snd_pcm_plugin_t **r_plugin) { + int err; stream_t *data; snd_pcm_plugin_t *plugin; @@ -107,17 +134,18 @@ int snd_pcm_plugin_build_stream(snd_pcm_t *pcm, int channel, *r_plugin = NULL; if (!pcm || channel < 0 || channel > 1) return -EINVAL; - plugin = snd_pcm_plugin_build(pcm, - channel == SND_PCM_CHANNEL_PLAYBACK ? - "I/O stream playback" : - "I/O stream capture", - format, format, - sizeof(stream_t) + sizeof(struct iovec) * format->voices); - if (plugin == NULL) - return -ENOMEM; + err = snd_pcm_plugin_build(pcm, channel, + "I/O stream", + format, format, + sizeof(stream_t) + sizeof(struct iovec) * format->voices, + &plugin); + if (err < 0) + return err; data = (stream_t *)plugin->extra_data; - data->channel = channel; + data->slave = slave; plugin->transfer = stream_transfer; + if (format->interleave && channel == SND_PCM_CHANNEL_PLAYBACK) + plugin->client_voices = stream_src_voices; *r_plugin = plugin; return 0; } diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index 490c44b2..88af755d 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -190,7 +190,7 @@ ssize_t snd_rawmidi_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size) { ssize_t result; - if (!rmidi || (!buffer && size > 0) || size < 0) + if (!rmidi || (!buffer && size > 0)) return -EINVAL; result = write(rmidi->fd, buffer, size); if (result < 0) @@ -202,7 +202,7 @@ ssize_t snd_rawmidi_read(snd_rawmidi_t *rmidi, void *buffer, size_t size) { ssize_t result; - if (!rmidi || (!buffer && size > 0) || size < 0) + if (!rmidi || (!buffer && size > 0)) return -EINVAL; result = read(rmidi->fd, buffer, size); if (result < 0) diff --git a/src/seq/seq.c b/src/seq/seq.c index 434b0542..bd40bba9 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -39,7 +39,7 @@ * prototypes */ static int snd_seq_free_event_static(snd_seq_event_t *ev); -static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev); +static int snd_seq_decode_event(char **buf, size_t *len, snd_seq_event_t *ev); /* @@ -184,7 +184,7 @@ int snd_seq_input_buffer_size(snd_seq_t *seq) /* * resize output buffer */ -int snd_seq_resize_output_buffer(snd_seq_t *seq, int size) +int snd_seq_resize_output_buffer(snd_seq_t *seq, size_t size) { if (!seq || !seq->obuf) return -EINVAL; @@ -206,7 +206,7 @@ int snd_seq_resize_output_buffer(snd_seq_t *seq, int size) /* * resize input buffer */ -int snd_seq_resize_input_buffer(snd_seq_t *seq, int size) +int snd_seq_resize_input_buffer(snd_seq_t *seq, size_t size) { if (!seq || !seq->ibuf) return -EINVAL; @@ -608,17 +608,14 @@ int snd_seq_free_event(snd_seq_event_t *ev) /* * calculates the (encoded) byte-stream size of the event */ -int snd_seq_event_length(snd_seq_event_t *ev) +ssize_t snd_seq_event_length(snd_seq_event_t *ev) { - int len = sizeof(snd_seq_event_t); + ssize_t len = sizeof(snd_seq_event_t); if (!ev) return -EINVAL; - if (snd_seq_ev_is_variable(ev)) { - if (ev->data.ext.len < 0) - return -EINVAL; + if (snd_seq_ev_is_variable(ev)) len += ev->data.ext.len; - } return len; } @@ -659,7 +656,7 @@ int snd_seq_event_output_buffer(snd_seq_t *seq, snd_seq_event_t *ev) len = snd_seq_event_length(ev); if (len < 0) return -EINVAL; - if ((seq->obufsize - seq->obufused) < len) + if ((seq->obufsize - seq->obufused) < (size_t) len) return -EAGAIN; memcpy(seq->obuf + seq->obufused, ev, sizeof(snd_seq_event_t)); seq->obufused += sizeof(snd_seq_event_t); @@ -722,7 +719,7 @@ int snd_seq_flush_output(snd_seq_t *seq) result = write(seq->fd, seq->obuf, seq->obufused); if (result < 0) return -errno; - if (result < seq->obufused) + if ((size_t)result < seq->obufused) memmove(seq->obuf, seq->obuf + result, seq->obufused - result); seq->obufused -= result; } @@ -853,7 +850,7 @@ static int snd_seq_input_cell_available(snd_seq_t *seq) /* * decode from byte-stream to an event record */ -static int snd_seq_decode_event(char **buf, int *len, snd_seq_event_t *ev) +static int snd_seq_decode_event(char **buf, size_t *len, snd_seq_event_t *ev) { if (!ev || !buf || !*buf || !len ) return -EINVAL; @@ -1213,11 +1210,11 @@ int snd_seq_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp) snd_seq_drain_output_buffer(seq); } else { char *ep; - int len; + size_t len; snd_seq_event_t *ev; ep = seq->obuf; - while (ep - seq->obuf < seq->obufused) { + while (ep - seq->obuf < (ssize_t)seq->obufused) { ev = (snd_seq_event_t *) ep; len = snd_seq_event_length(ev); diff --git a/src/seq/seq_priv.h b/src/seq/seq_priv.h index 1f35642e..93e754ef 100644 --- a/src/seq/seq_priv.h +++ b/src/seq/seq_priv.h @@ -32,10 +32,10 @@ struct snd_seq { int fd; /* buffers */ char *obuf; /* output buffer */ - int obufsize; /* output buffer size */ - int obufused; /* output buffer used size */ + size_t obufsize; /* output buffer size */ + size_t obufused; /* output buffer used size */ char *ibuf; /* input buffer */ - int ibufsize; /* input buffer size */ + size_t ibufsize; /* input buffer size */ /* input queue */ int cells; snd_seq_cell_t *head; diff --git a/src/timer/timer.c b/src/timer/timer.c index e99e3ddc..41f42d7c 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -202,7 +202,7 @@ ssize_t snd_timer_read(snd_timer_t *handle, void *buffer, size_t size) ssize_t result; tmr = handle; - if (!tmr || (!buffer && size > 0) || size < 0) + if (!tmr || (!buffer && size > 0)) return -EINVAL; result = read(tmr->fd, buffer, size); if (result < 0) diff --git a/test/latency.c b/test/latency.c index e0b6991c..b531b935 100644 --- a/test/latency.c +++ b/test/latency.c @@ -10,8 +10,11 @@ #endif #define USED_RATE 48000 -#define LATENCY_LIMIT 8192 /* in bytes */ -#define LOOP_LIMIT (30 * 176400) /* 30 seconds */ +//#define LATENCY_MIN 8192 +#define LATENCY_MIN 32 +#define LATENCY_MAX 8192 /* in bytes */ +//#define LOOP_LIMIT (8192UL * 2) +#define LOOP_LIMIT (30 * 176400UL) /* 30 seconds */ #if 0 static char *xitoa(int aaa) @@ -32,7 +35,6 @@ static int syncro(snd_pcm_t *phandle, snd_pcm_t *chandle) bzero(&cinfo, sizeof(cinfo)); pinfo.channel = SND_PCM_CHANNEL_PLAYBACK; cinfo.channel = SND_PCM_CHANNEL_CAPTURE; - pinfo.mode = cinfo.mode = SND_PCM_MODE_STREAM; if ((err = snd_pcm_channel_info(phandle, &pinfo)) < 0) { printf("Playback info error: %s\n", snd_strerror(err)); exit(0); @@ -83,7 +85,7 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int sync, int *queue) params.format.voices = 2; params.format.rate = USED_RATE; params.start_mode = SND_PCM_START_GO; - params.stop_mode = SND_PCM_STOP_STOP; + params.xrun_mode = SND_PCM_XRUN_DRAIN; params.time = 1; *queue += 16; #if 0 @@ -95,46 +97,50 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int sync, int *queue) if (sync) syncro_id(¶ms.sync); __again: - if (*queue > LATENCY_LIMIT) + if (*queue > LATENCY_MAX) return -1; again = 0; params.channel = SND_PCM_CHANNEL_PLAYBACK; + params.frag_size = *queue; #ifdef USE_BLOCK_MODE - params.buf.block.frag_size = *queue; + params.buffer_size = *queue * 2; params.buf.block.frags_min = 1; - params.buf.block.frags_max = -1; #else - params.buf.stream.queue_size = *queue; + params.buffer_size = *queue; + params.buf.stream.bytes_min = 2; #endif - if ((err = snd_pcm_plugin_params(phandle, ¶ms)) < 0) { + if ((err = snd_pcm_channel_params(phandle, ¶ms)) < 0) { printf("Playback params error: %s\n", snd_strerror(err)); exit(0); } params.channel = SND_PCM_CHANNEL_CAPTURE; - if ((err = snd_pcm_plugin_params(chandle, ¶ms)) < 0) { + if ((err = snd_pcm_channel_params(chandle, ¶ms)) < 0) { printf("Capture params error: %s\n", snd_strerror(err)); exit(0); } bzero(&psetup, sizeof(psetup)); psetup.channel = SND_PCM_CHANNEL_PLAYBACK; - if ((err = snd_pcm_plugin_setup(phandle, &psetup)) < 0) { + if ((err = snd_pcm_channel_setup(phandle, &psetup)) < 0) { printf("Playback setup error: %s\n", snd_strerror(err)); exit(0); } bzero(&csetup, sizeof(csetup)); csetup.channel = SND_PCM_CHANNEL_CAPTURE; - if ((err = snd_pcm_plugin_setup(chandle, &csetup)) < 0) { + if ((err = snd_pcm_channel_setup(chandle, &csetup)) < 0) { printf("Capture setup error: %s\n", snd_strerror(err)); exit(0); } - if (psetup.buf.stream.queue_size > *queue) { - *queue = psetup.buf.stream.queue_size; +#ifdef USE_BLOCK_MODE + if (psetup.buffer_size / 2 > *queue) { + *queue = psetup.buffer_size / 2; again++; } - if (csetup.buf.stream.queue_size > *queue) { - *queue = csetup.buf.stream.queue_size; +#else + if (psetup.buffer_size > *queue) { + *queue = psetup.buffer_size; again++; } +#endif if (again) goto __again; if ((err = snd_pcm_playback_prepare(phandle)) < 0) { @@ -152,11 +158,13 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int sync, int *queue) *queue, #endif psetup.format.rate, csetup.format.rate); + printf("Fragment boundary = %li/%li, Position boundary = %li/%li\n", (long)psetup.frag_boundary, (long)csetup.frag_boundary, (long)psetup.pos_boundary, (long)psetup.pos_boundary); + printf("Frags = %li/%li, Buffer size = %li/%li\n", (long)psetup.frags, (long)csetup.frags, (long)psetup.buffer_size, (long)csetup.buffer_size); fflush(stdout); return 0; } -void showstat(snd_pcm_t *handle, int channel, snd_pcm_channel_status_t *rstatus) +void showstat(snd_pcm_t *handle, int channel, snd_pcm_channel_status_t *rstatus, size_t bytes) { int err; snd_pcm_channel_status_t status; @@ -165,16 +173,21 @@ void showstat(snd_pcm_t *handle, int channel, snd_pcm_channel_status_t *rstatus) str = channel == SND_PCM_CHANNEL_CAPTURE ? "Capture" : "Playback"; bzero(&status, sizeof(status)); status.channel = channel; - status.mode = SND_PCM_MODE_STREAM; - if ((err = snd_pcm_plugin_status(handle, &status)) < 0) { + if ((err = snd_pcm_channel_status(handle, &status)) < 0) { printf("Channel %s status error: %s\n", str, snd_strerror(err)); exit(0); } printf("%s:\n", str); printf(" status = %i\n", status.status); - printf(" position = %u\n", status.scount); - printf(" free = %i\n", status.free); - printf(" count = %i\n", status.count); + printf(" bytes = %i\n", bytes); + printf(" frag_io = %li\n", (long)status.frag_io); + printf(" frag_data = %li\n", (long)status.frag_data); + printf(" frag_used = %li\n", (long)status.frags_used); + printf(" frag_free = %li\n", (long)status.frags_free); + printf(" pos_io = %li\n", (long)status.pos_io); + printf(" pos_data = %li\n", (long)status.pos_data); + printf(" bytes_used = %li\n", (long)status.bytes_used); + printf(" bytes_free = %li\n", (long)status.bytes_free); if (rstatus) *rstatus = status; } @@ -210,24 +223,26 @@ long timediff(struct timeval t1, struct timeval t2) return (t1.tv_sec * 1000000) + l; } -long readbuf(snd_pcm_t *handle, char *buf, long len) +long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *bytes) { long r; do { - r = snd_pcm_plugin_read(handle, buf, len); + r = snd_pcm_read(handle, buf, len); } while (r == -EAGAIN); + if (r > 0) + *bytes += r; // printf("read = %li\n", r); // showstat(handle, SND_PCM_CHANNEL_CAPTURE, NULL); return r; } -long writebuf(snd_pcm_t *handle, char *buf, long len) +long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *bytes) { long r; while (len > 0) { - r = snd_pcm_plugin_write(handle, buf, len); + r = snd_pcm_write(handle, buf, len); #ifndef USE_BLOCK_MODE if (r == -EAGAIN) continue; @@ -238,6 +253,7 @@ long writebuf(snd_pcm_t *handle, char *buf, long len) // showstat(handle, SND_PCM_CHANNEL_PLAYBACK, NULL); buf += r; len -= r; + *bytes += r; } return 0; } @@ -245,23 +261,23 @@ long writebuf(snd_pcm_t *handle, char *buf, long len) int main(void) { snd_pcm_t *phandle, *chandle; - char buffer[4096 * 2]; /* max two fragments by 4096 bytes */ + char buffer[LATENCY_MAX]; /* max two fragments by 4096 bytes */ int pcard = 0, pdevice = 0; int ccard = 0, cdevice = 0; - int err, latency = 16; + int err, latency = LATENCY_MIN - 16; int size, ok; int sync; snd_pcm_sync_t ssync; snd_pcm_channel_status_t pstatus, cstatus; - long r; + ssize_t r; + size_t bytes_in, bytes_out; - // latency = 4096 - 16; setscheduler(); - if ((err = snd_pcm_open(&phandle, pcard, pdevice, SND_PCM_OPEN_PLAYBACK)) < 0) { + if ((err = snd_pcm_plug_open(&phandle, pcard, pdevice, SND_PCM_OPEN_PLAYBACK|SND_PCM_NONBLOCK_PLAYBACK)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); return 0; } - if ((err = snd_pcm_open(&chandle, ccard, cdevice, SND_PCM_OPEN_CAPTURE)) < 0) { + if ((err = snd_pcm_plug_open(&chandle, ccard, cdevice, SND_PCM_OPEN_CAPTURE|SND_PCM_NONBLOCK_CAPTURE)) < 0) { printf("Record open error: %s\n", snd_strerror(err)); return 0; } @@ -273,14 +289,21 @@ int main(void) sync = syncro(phandle, chandle); if (sync) printf("Using hardware synchronization mode\n"); + printf("Loop limit is %li bytes\n", LOOP_LIMIT); while (1) { + bytes_in = bytes_out = 0; if (setparams(phandle, chandle, sync, &latency) < 0) break; - memset(buffer, 0, latency); - if (writebuf(phandle, buffer, latency) < 0) + if (snd_pcm_format_set_silence(SND_PCM_SFMT_S16_LE, buffer, latency) < 0) { + fprintf(stderr, "silence error\n"); break; + } + if (writebuf(phandle, buffer, latency, &bytes_out) < 0) { + fprintf(stderr, "write error\n"); + break; + } #ifdef USE_BLOCK_MODE /* at least two fragments MUST BE filled !!! */ - if (writebuf(phandle, buffer, latency) < 0) + if (writebuf(phandle, buffer, latency, &bytes_out) < 0) break; #endif if (sync) { @@ -301,15 +324,14 @@ int main(void) } ok = 1; size = 0; - while (ok && size < LOOP_LIMIT) { - if ((r = readbuf(chandle, buffer, latency)) < 0) + while (ok && bytes_in < LOOP_LIMIT) { + if ((r = readbuf(chandle, buffer, latency, &bytes_in)) < 0) ok = 0; - if (writebuf(phandle, buffer, r) < 0) + if (r > 0 && writebuf(phandle, buffer, r, &bytes_out) < 0) ok = 0; - size += r; } - showstat(phandle, SND_PCM_CHANNEL_PLAYBACK, &pstatus); - showstat(chandle, SND_PCM_CHANNEL_CAPTURE, &cstatus); + showstat(phandle, SND_PCM_CHANNEL_PLAYBACK, &pstatus, bytes_out); + showstat(chandle, SND_PCM_CHANNEL_CAPTURE, &cstatus, bytes_in); snd_pcm_capture_flush(chandle); snd_pcm_playback_flush(phandle); if (ok) {