diff options
61 files changed, 7098 insertions, 6669 deletions
@@ -1,4 +1,4 @@ +H solve multi/server mmap'ing problem +H add now_ptr stuff and fix appl_ptr seek M add abstraction layer to timer, rawmidi, hwdep, seq -M plug sync and pos problems -M Loopback implementation? L move OSS emulation to user space? (pseudo device driver and daemon) diff --git a/acinclude.m4 b/acinclude.m4 index 59ef07e9..1bf50ad9 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -10,7 +10,7 @@ void main(void) #if !defined(SND_PROTOCOL_VERSION) || !defined(SND_PROTOCOL_INCOMPATIBLE) #error not found #else -#if !defined(SND_PCM_IOCTL_HW_PTR) +#if !defined(SND_PCM_IOCTL_APPL_PTR) #error wrong version #endif exit(0); diff --git a/include/aserver.h b/include/aserver.h index fb7164ed..aef553a8 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -19,13 +19,16 @@ */ -#define SND_PCM_IOCTL_MMAP_DATA _IO ('A', 0xf0) -#define SND_PCM_IOCTL_MMAP_CONTROL _IO ('A', 0xf1) -#define SND_PCM_IOCTL_MMAP_STATUS _IO ('A', 0xf2) -#define SND_PCM_IOCTL_MUNMAP_DATA _IO ('A', 0xf3) -#define SND_PCM_IOCTL_MUNMAP_CONTROL _IO ('A', 0xf4) -#define SND_PCM_IOCTL_MUNMAP_STATUS _IO ('A', 0xf5) -#define SND_PCM_IOCTL_CLOSE _IO ('A', 0xf6) +#define SND_PCM_IOCTL_STATE _IO ('A', 0xf0) +#define SND_PCM_IOCTL_MMAP_DATA _IO ('A', 0xf1) +#define SND_PCM_IOCTL_MMAP_CONTROL _IO ('A', 0xf2) +#define SND_PCM_IOCTL_MMAP_STATUS _IO ('A', 0xf3) +#define SND_PCM_IOCTL_MUNMAP_DATA _IO ('A', 0xf4) +#define SND_PCM_IOCTL_MUNMAP_CONTROL _IO ('A', 0xf5) +#define SND_PCM_IOCTL_MUNMAP_STATUS _IO ('A', 0xf6) +#define SND_PCM_IOCTL_MMAP_FORWARD _IOW('A', 0xf7, size_t) +#define SND_PCM_IOCTL_AVAIL_UPDATE _IO ('A', 0xf8) +#define SND_PCM_IOCTL_CLOSE _IO ('A', 0xf9) typedef struct { int result; @@ -36,6 +39,7 @@ typedef struct { snd_pcm_params_info_t params_info; snd_pcm_setup_t setup; snd_pcm_status_t status; + ssize_t delay; int pause; snd_pcm_channel_info_t channel_info; snd_pcm_channel_params_t channel_params; @@ -43,10 +47,7 @@ typedef struct { off_t appl_ptr; int hw_ptr; int link; - snd_xfer_t read; - snd_xfer_t write; - snd_xferv_t readv; - snd_xferv_t writev; + size_t mmap_forward; } u; char data[0]; } snd_pcm_client_shm_t; diff --git a/include/control.h b/include/control.h index f68b5ac0..07ad251e 100644 --- a/include/control.h +++ b/include/control.h @@ -42,7 +42,7 @@ int snd_ctl_client_open(snd_ctl_t **handlep, char *host, int port, int transport snd_ctl_type_t snd_ctl_type(snd_ctl_t *handle); int snd_ctl_open(snd_ctl_t **handle, char *name); int snd_ctl_close(snd_ctl_t *handle); -int snd_ctl_file_descriptor(snd_ctl_t *handle); +int snd_ctl_poll_descriptor(snd_ctl_t *handle); int snd_ctl_hw_info(snd_ctl_t *handle, snd_ctl_hw_info_t *info); int snd_ctl_clist(snd_ctl_t *handle, snd_control_list_t * list); int snd_ctl_cinfo(snd_ctl_t *handle, snd_control_info_t * sw); diff --git a/include/header.h b/include/header.h index 6c667bd2..6875e256 100644 --- a/include/header.h +++ b/include/header.h @@ -30,6 +30,7 @@ #include <stdlib.h> #include <string.h> #include <fcntl.h> +#include <sys/uio.h> #ifndef ATTRIBUTE_UNUSED #define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) diff --git a/include/hwdep.h b/include/hwdep.h index 81918192..6446543b 100644 --- a/include/hwdep.h +++ b/include/hwdep.h @@ -18,7 +18,7 @@ typedef struct snd_hwdep snd_hwdep_t; int snd_hwdep_open(snd_hwdep_t **handle, int card, int device, int mode); int snd_hwdep_close(snd_hwdep_t *handle); -int snd_hwdep_file_descriptor(snd_hwdep_t *handle); +int snd_hwdep_poll_descriptor(snd_hwdep_t *handle); int snd_hwdep_block_mode(snd_hwdep_t *handle, int enable); int snd_hwdep_info(snd_hwdep_t *handle, snd_hwdep_info_t * info); int snd_hwdep_ioctl(snd_hwdep_t *handle, int request, void * arg); diff --git a/include/mixer.h b/include/mixer.h index 1daef64e..f7eab69b 100644 --- a/include/mixer.h +++ b/include/mixer.h @@ -13,7 +13,7 @@ extern "C" { int snd_mixer_open(snd_mixer_t **handle, char *name); int snd_mixer_close(snd_mixer_t *handle); -int snd_mixer_file_descriptor(snd_mixer_t *handle); +int snd_mixer_poll_descriptor(snd_mixer_t *handle); #ifdef __cplusplus } diff --git a/include/pcm.h b/include/pcm.h index 25a20e02..a559176e 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -98,18 +98,42 @@ static inline size_t bitset_count(bitset_t *bitset, size_t nbits) 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_MULTI, SND_PCM_TYPE_CLIENT } snd_pcm_type_t; +typedef enum { + SND_PCM_TYPE_HW, + SND_PCM_TYPE_MULTI, + SND_PCM_TYPE_FILE, + SND_PCM_TYPE_NULL, + SND_PCM_TYPE_CLIENT, + SND_PCM_TYPE_LINEAR, + SND_PCM_TYPE_ALAW, + SND_PCM_TYPE_MULAW, + SND_PCM_TYPE_ADPCM, + SND_PCM_TYPE_RATE, + SND_PCM_TYPE_ROUTE, + SND_PCM_TYPE_COPY, + SND_PCM_TYPE_PLUG, + SND_PCM_TYPE_DROUTE, + SND_PCM_TYPE_LBSERVER, +} snd_pcm_type_t; int snd_pcm_open(snd_pcm_t **handle, char *name, int stream, int mode); +/* Obsolete functions */ int snd_pcm_hw_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode); -int snd_pcm_hw_open(snd_pcm_t **handle, int card, int device, int stream, int mode); +int snd_pcm_hw_open_device(snd_pcm_t **handle, int card, int device, int stream, int mode); +int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode); +int snd_pcm_plug_open_device(snd_pcm_t **handle, int card, int device, int stream, int mode); +#define snd_pcm_write snd_pcm_writei +#define snd_pcm_read snd_pcm_readi +ssize_t snd_pcm_writev(snd_pcm_t *handle, const struct iovec *vector, int count); +ssize_t snd_pcm_readv(snd_pcm_t *handle, const struct iovec *vector, int count); + snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle); int snd_pcm_close(snd_pcm_t *handle); -int snd_pcm_file_descriptor(snd_pcm_t *handle); +int snd_pcm_poll_descriptor(snd_pcm_t *handle); int snd_pcm_nonblock(snd_pcm_t *handle, int nonblock); int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info); int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info); @@ -120,65 +144,55 @@ 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_status(snd_pcm_t *handle, snd_pcm_status_t *status); int snd_pcm_prepare(snd_pcm_t *handle); -int snd_pcm_go(snd_pcm_t *handle); -int snd_pcm_drain(snd_pcm_t *handle); +int snd_pcm_start(snd_pcm_t *handle); +int snd_pcm_stop(snd_pcm_t *handle); int snd_pcm_flush(snd_pcm_t *handle); int snd_pcm_pause(snd_pcm_t *handle, int enable); int snd_pcm_state(snd_pcm_t *handle); -ssize_t snd_pcm_hw_ptr(snd_pcm_t *handle, int update); +int snd_pcm_delay(snd_pcm_t *handle, ssize_t *delayp); +size_t snd_pcm_hw_ptr(snd_pcm_t *handle); ssize_t snd_pcm_appl_ptr(snd_pcm_t *handle, off_t offset); -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 *handle, const struct iovec *vector, unsigned long count); -ssize_t snd_pcm_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long count); +ssize_t snd_pcm_writei(snd_pcm_t *handle, const void *buffer, size_t size); +ssize_t snd_pcm_readi(snd_pcm_t *handle, void *buffer, size_t size); +ssize_t snd_pcm_writen(snd_pcm_t *handle, void **bufs, size_t size); +ssize_t snd_pcm_readn(snd_pcm_t *handle, void **bufs, size_t size); int snd_pcm_dump_setup(snd_pcm_t *handle, FILE *fp); int snd_pcm_dump(snd_pcm_t *handle, FILE *fp); +int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp); int snd_pcm_link(snd_pcm_t *handle1, snd_pcm_t *handle2); int snd_pcm_unlink(snd_pcm_t *handle); -int snd_pcm_channels_mask(snd_pcm_t *handle, bitset_t *client_vmask); +int snd_pcm_channels_mask(snd_pcm_t *handle, bitset_t *cmask); +int snd_pcm_wait(snd_pcm_t *pcm, int timeout); +ssize_t snd_pcm_avail_update(snd_pcm_t *pcm); + /* mmap */ -int snd_pcm_mmap(snd_pcm_t *handle, snd_pcm_mmap_status_t **status, snd_pcm_mmap_control_t **control, void **buffer); +int snd_pcm_mmap(snd_pcm_t *handle, void **buffer); int snd_pcm_munmap(snd_pcm_t *handle); -int snd_pcm_mmap_state(snd_pcm_t *handle); -ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *handle); -ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *handle, off_t offset); -int snd_pcm_mmap_status(snd_pcm_t *handle, snd_pcm_mmap_status_t **status); -int snd_pcm_mmap_control(snd_pcm_t *handle, snd_pcm_mmap_control_t **control); -int snd_pcm_mmap_data(snd_pcm_t *handle, void **buffer); -int snd_pcm_munmap_status(snd_pcm_t *handle); -int snd_pcm_munmap_control(snd_pcm_t *handle); -int snd_pcm_munmap_data(snd_pcm_t *handle); -int snd_pcm_mmap_ready(snd_pcm_t *handle); -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 *handle, const struct iovec *vector, unsigned long count); -ssize_t snd_pcm_mmap_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long count); -int snd_pcm_mmap_avail(snd_pcm_t *handle, ssize_t *frames); -ssize_t snd_pcm_mmap_xfer(snd_pcm_t *handle, size_t frames); -ssize_t snd_pcm_mmap_offset(snd_pcm_t *handle); -ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames); -ssize_t snd_pcm_mmap_write_frames(snd_pcm_t *handle, const void *buffer, size_t frames); -ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames); -ssize_t snd_pcm_mmap_read_frames(snd_pcm_t *handle, const void *buffer, size_t frames); int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas); - +ssize_t snd_pcm_mmap_forward(snd_pcm_t *pcm, size_t size); +size_t snd_pcm_mmap_offset(snd_pcm_t *pcm); +size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t size); +ssize_t snd_pcm_mmap_writei(snd_pcm_t *handle, const void *buffer, size_t size); +ssize_t snd_pcm_mmap_readi(snd_pcm_t *handle, void *buffer, size_t size); +ssize_t snd_pcm_mmap_writen(snd_pcm_t *handle, void **bufs, size_t size); +ssize_t snd_pcm_mmap_readn(snd_pcm_t *handle, void **bufs, size_t size); const char *snd_pcm_format_name(int format); const char *snd_pcm_format_description(int format); int snd_pcm_format_value(const char* name); -int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, +int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_channel, size_t dst_offset, size_t samples, int format); -int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_channels, size_t dst_offset, +int snd_pcm_areas_silence(snd_pcm_channel_area_t *dst_channels, size_t dst_offset, size_t vcount, size_t frames, int format); -int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_channel, size_t src_offset, - const snd_pcm_channel_area_t *dst_channel, size_t dst_offset, +int snd_pcm_area_copy(snd_pcm_channel_area_t *src_channel, size_t src_offset, + snd_pcm_channel_area_t *dst_channel, size_t dst_offset, size_t samples, int format); -int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_channels, size_t src_offset, - const snd_pcm_channel_area_t *dst_channels, size_t dst_offset, - size_t vcount, size_t frames, int format); +int snd_pcm_areas_copy(snd_pcm_channel_area_t *src_channels, size_t src_offset, + snd_pcm_channel_area_t *dst_channels, size_t dst_offset, + size_t channels, size_t frames, int format); ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes); ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, ssize_t frames); @@ -207,165 +221,3 @@ ssize_t snd_pcm_format_set_silence(int format, void *buf, size_t count); } #endif -/* - * PCM Plug-In interface - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct snd_stru_pcm_plugin snd_pcm_plugin_t; -#define snd_pcm_plug_t struct snd_pcm_plug - -typedef enum { - INIT = 0, - PREPARE = 1, - DRAIN = 2, - FLUSH = 3, - PAUSE = 4, -} snd_pcm_plugin_action_t; - -typedef struct snd_stru_pcm_plugin_channel { - snd_pcm_channel_area_t area; - unsigned int enabled:1; /* channel need to be processed */ - unsigned int wanted:1; /* channel is wanted */ -} snd_pcm_plugin_channel_t; - -struct snd_stru_pcm_plugin { - char *name; /* plug-in name */ - int stream; - 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_frames)(snd_pcm_plugin_t *plugin, size_t dst_frames); - ssize_t (*dst_frames)(snd_pcm_plugin_t *plugin, size_t src_frames); - ssize_t (*client_channels)(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels); - int (*src_channels_mask)(snd_pcm_plugin_t *plugin, - bitset_t *dst_vmask, - bitset_t **src_vmask); - int (*dst_channels_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_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames); - int (*action)(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_action_t action, - unsigned long data); - int (*parameter_set)(snd_pcm_plugin_t *plugin, - const char *name, - unsigned long value); - int (*parameter_get)(snd_pcm_plugin_t *plugin, - const char *name, - unsigned long *value); - void (*dump)(snd_pcm_plugin_t *plugin, FILE *fp); - snd_pcm_plugin_t *prev; - snd_pcm_plugin_t *next; - snd_pcm_plug_t *plug; - void *private_data; - void (*private_free)(snd_pcm_plugin_t *plugin); - char *buf; - size_t buf_frames; - snd_pcm_plugin_channel_t *buf_channels; - bitset_t *src_vmask; - bitset_t *dst_vmask; - char extra_data[0]; -}; - -int snd_pcm_plug_create(snd_pcm_t **handle, snd_pcm_t *slave, int close_slave); -int snd_pcm_plug_open_subdevice(snd_pcm_t **handle, int card, int device, int subdevice, int stream, int mode); -int snd_pcm_plug_open(snd_pcm_t **handle, int card, int device, int stream, int mode); - -int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin); -int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin); -int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin); -void snd_pcm_plugin_dump(snd_pcm_plugin_t *plugin, FILE *fp); -int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, size_t frames); -int snd_pcm_plug_clear(snd_pcm_plug_t *plug); -snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_plug_t *plug); -snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_plug_t *plug); -int snd_pcm_plug_direct(snd_pcm_plug_t *plug); -ssize_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, size_t drv_frames); -ssize_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, size_t clt_frames); - -/* - * Plug-In constructors - */ - -int snd_pcm_plugin_build(snd_pcm_plug_t *plug, - const char *name, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - size_t extra, - snd_pcm_plugin_t **ret); -/* basic I/O */ -int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_mmap(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_plugin_t **r_plugin); - -#define ROUTE_PLUGIN_USE_FLOAT 1 -#if ROUTE_PLUGIN_USE_FLOAT -#define FULL 1.0 -#define HALF 0.5 -typedef float route_ttable_entry_t; -#else -#define FULL ROUTE_PLUGIN_RESOLUTION -#define HALF ROUTE_PLUGIN_RESOLUTION / 2 -typedef int route_ttable_entry_t; -#endif - -/* conversion plugins */ -int snd_pcm_plugin_build_interleave(snd_pcm_plug_t *plug, - 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_plug_t *plug, - 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_plug_t *plug, - 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_plug_t *plug, - 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_plug_t *plug, - 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_plug_t *plug, - 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_plug_t *plug, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - route_ttable_entry_t *ttable, - snd_pcm_plugin_t **r_plugin); -int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - snd_pcm_plugin_t **r_plugin); - -int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, - snd_pcm_t **slaves_handle, size_t *slaves_channels_count, - size_t binds_count, unsigned int *binds_client_channel, - unsigned int *binds_slave, unsigned int *binds_slave_channel, - int close_slaves); - -int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transport, char *name, int stream, int mode); - -#ifdef __cplusplus -} -#endif - diff --git a/include/rawmidi.h b/include/rawmidi.h index 087b8821..5bdab976 100644 --- a/include/rawmidi.h +++ b/include/rawmidi.h @@ -20,7 +20,7 @@ typedef struct snd_rawmidi snd_rawmidi_t; int snd_rawmidi_open(snd_rawmidi_t **handle, int card, int device, int mode); int snd_rawmidi_close(snd_rawmidi_t *handle); -int snd_rawmidi_file_descriptor(snd_rawmidi_t *handle); +int snd_rawmidi_poll_descriptor(snd_rawmidi_t *handle); int snd_rawmidi_block_mode(snd_rawmidi_t *handle, int enable); int snd_rawmidi_info(snd_rawmidi_t *handle, snd_rawmidi_info_t * info); int snd_rawmidi_stream_params(snd_rawmidi_t *handle, snd_rawmidi_params_t * params); diff --git a/include/seq.h b/include/seq.h index 7e358060..3bd4989d 100644 --- a/include/seq.h +++ b/include/seq.h @@ -17,7 +17,7 @@ typedef struct snd_seq snd_seq_t; int snd_seq_open(snd_seq_t **handle, int mode); int snd_seq_close(snd_seq_t *handle); -int snd_seq_file_descriptor(snd_seq_t *handle); +int snd_seq_poll_descriptor(snd_seq_t *handle); 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); diff --git a/include/timer.h b/include/timer.h index 49c69437..a406d9c0 100644 --- a/include/timer.h +++ b/include/timer.h @@ -13,7 +13,7 @@ typedef struct snd_timer snd_timer_t; int snd_timer_open(snd_timer_t **handle); int snd_timer_close(snd_timer_t *handle); -int snd_timer_file_descriptor(snd_timer_t *handle); +int snd_timer_poll_descriptor(snd_timer_t *handle); int snd_timer_general_info(snd_timer_t *handle, snd_timer_general_info_t * info); int snd_timer_select(snd_timer_t *handle, snd_timer_select_t *tselect); int snd_timer_info(snd_timer_t *handle, snd_timer_info_t *timer); diff --git a/src/Makefile.am b/src/Makefile.am index 60eb8612..c6985ed6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,7 +6,7 @@ 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 \ - compat/libcompat.la conf/libconf.la + compat/libcompat.la conf/libconf.la -ldl libasound_la_LDFLAGS = -version-info $(COMPATNUM) diff --git a/src/aserver/aserver.c b/src/aserver/aserver.c index a4e7449b..4a77a952 100644 --- a/src/aserver/aserver.c +++ b/src/aserver/aserver.c @@ -287,7 +287,7 @@ int pcm_shm_open(client_t *client, int *cookie) if (err < 0) return err; client->device.pcm.handle = pcm; - client->device.pcm.fd = snd_pcm_file_descriptor(pcm); + client->device.pcm.fd = snd_pcm_poll_descriptor(pcm); shmid = shmget(IPC_PRIVATE, PCM_SHM_SIZE, 0666); if (shmid < 0) { @@ -368,20 +368,26 @@ int pcm_shm_cmd(client_t *client) case SND_PCM_IOCTL_STATUS: ctrl->result = snd_pcm_status(pcm, &ctrl->u.status); break; - case SND_PCM_IOCTL_HW_PTR: - ctrl->result = snd_pcm_hw_ptr(pcm, ctrl->u.hw_ptr); + case SND_PCM_IOCTL_STATE: + ctrl->result = snd_pcm_state(pcm); + break; + case SND_PCM_IOCTL_DELAY: + ctrl->result = snd_pcm_delay(pcm, &ctrl->u.delay); + break; + case SND_PCM_IOCTL_AVAIL_UPDATE: + ctrl->result = snd_pcm_avail_update(pcm); break; case SND_PCM_IOCTL_PREPARE: ctrl->result = snd_pcm_prepare(pcm); break; - case SND_PCM_IOCTL_GO: - ctrl->result = snd_pcm_go(pcm); + case SND_PCM_IOCTL_START: + ctrl->result = snd_pcm_start(pcm); break; case SND_PCM_IOCTL_FLUSH: ctrl->result = snd_pcm_flush(pcm); break; - case SND_PCM_IOCTL_DRAIN: - ctrl->result = snd_pcm_drain(pcm); + case SND_PCM_IOCTL_STOP: + ctrl->result = snd_pcm_stop(pcm); break; case SND_PCM_IOCTL_PAUSE: ctrl->result = snd_pcm_pause(pcm, ctrl->u.pause); @@ -395,86 +401,6 @@ int pcm_shm_cmd(client_t *client) case SND_PCM_IOCTL_CHANNEL_SETUP: ctrl->result = snd_pcm_channel_setup(pcm, &ctrl->u.channel_setup); break; - case SND_PCM_IOCTL_WRITE_FRAMES: - { - size_t maxsize = PCM_SHM_DATA_MAXLEN; - maxsize = snd_pcm_bytes_to_frames(pcm, maxsize); - if (ctrl->u.write.count > maxsize) { - ctrl->result = -EFAULT; - break; - } - /* FIXME: blocking */ - ctrl->result = snd_pcm_write(pcm, ctrl->data, ctrl->u.write.count); - break; - } - case SND_PCM_IOCTL_READ_FRAMES: - { - size_t maxsize = PCM_SHM_DATA_MAXLEN; - maxsize = snd_pcm_bytes_to_frames(pcm, maxsize); - if (ctrl->u.read.count > maxsize) { - ctrl->result = -EFAULT; - break; - } - /* FIXME: blocking */ - ctrl->result = snd_pcm_read(pcm, ctrl->data, ctrl->u.read.count); - break; - } - case SND_PCM_IOCTL_WRITEV_FRAMES: - { - /* FIXME: interleaved */ - size_t maxsize = PCM_SHM_DATA_MAXLEN; - unsigned long k; - struct iovec *vector; - size_t vecsize; - char *base; - int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8); - vecsize = ctrl->u.writev.count * sizeof(struct iovec); - if (vecsize > maxsize) { - ctrl->result = -EFAULT; - break; - } - maxsize -= vecsize; - vector = (struct iovec *) ctrl->data; - base = ctrl->data + vecsize; - for (k = 0; k < ctrl->u.writev.count; ++k) { - unsigned long ofs = (unsigned long) vector[k].iov_base; - size_t len = vector[k].iov_len * bits_per_sample / 8; - if (ofs + len > maxsize) - return -EFAULT; - vector[k].iov_base = base + ofs; - } - /* FIXME: blocking */ - ctrl->result = snd_pcm_writev(pcm, vector, ctrl->u.writev.count); - break; - } - case SND_PCM_IOCTL_READV_FRAMES: - { - /* FIXME: interleaved */ - size_t maxsize = PCM_SHM_DATA_MAXLEN; - unsigned long k; - struct iovec *vector; - size_t vecsize; - char *base; - int bits_per_sample = snd_pcm_samples_to_bytes(pcm, 8); - vecsize = ctrl->u.readv.count * sizeof(struct iovec); - if (vecsize > maxsize) { - ctrl->result = -EFAULT; - break; - } - maxsize -= vecsize; - vector = (struct iovec *) ctrl->data; - base = ctrl->data + vecsize; - for (k = 0; k < ctrl->u.readv.count; ++k) { - unsigned long ofs = (unsigned long) vector[k].iov_base; - size_t len = vector[k].iov_len * bits_per_sample / 8; - if (ofs + len > maxsize) - return -EFAULT; - vector[k].iov_base = base + ofs; - } - /* FIXME: blocking */ - ctrl->result = snd_pcm_readv(pcm, vector, ctrl->u.readv.count); - break; - } case SND_PCM_IOCTL_APPL_PTR: ctrl->result = snd_pcm_appl_ptr(pcm, ctrl->u.appl_ptr); break; @@ -510,13 +436,14 @@ int pcm_shm_cmd(client_t *client) ctrl->result = 0; return 0; } -#if 0 case SND_PCM_IOCTL_MUNMAP_DATA: case SND_PCM_IOCTL_MUNMAP_CONTROL: case SND_PCM_IOCTL_MUNMAP_STATUS: ctrl->result = 0; break; -#endif + case SND_PCM_IOCTL_MMAP_FORWARD: + ctrl->result = snd_pcm_mmap_forward(pcm, ctrl->u.mmap_forward); + break; case SND_PCM_IOCTL_CLOSE: client->ops->close(client); break; @@ -571,7 +498,7 @@ int ctl_shm_open(client_t *client, int *cookie) if (err < 0) return err; client->device.control.handle = ctl; - client->device.control.fd = snd_ctl_file_descriptor(ctl); + client->device.control.fd = snd_ctl_poll_descriptor(ctl); shmid = shmget(IPC_PRIVATE, CTL_SHM_SIZE, 0666); if (shmid < 0) { diff --git a/src/conf/conf.c b/src/conf/conf.c index 4607b6d5..de4e6bfb 100644 --- a/src/conf/conf.c +++ b/src/conf/conf.c @@ -186,7 +186,7 @@ static int get_quotedchar(input_t *input) } } -static int get_freestring(char **string, input_t *input) +static int get_freestring(char **string, int id, input_t *input) { const size_t bufsize = 256; char _buf[bufsize]; @@ -197,13 +197,15 @@ static int get_freestring(char **string, input_t *input) while (1) { c = get_char(input); switch (c) { + case '.': + if (!id) + break; case ' ': case '\f': case '\t': case '\n': case '\r': case EOF: - case '.': case '=': case '{': case '}': @@ -283,7 +285,7 @@ static int get_delimstring(char **string, int delim, input_t *input) } /* Return 0 for free string, 1 for delimited string */ -static int get_string(char **string, input_t *input) +static int get_string(char **string, int id, input_t *input) { int c = get_nonwhite(input); int err; @@ -312,7 +314,7 @@ static int get_string(char **string, input_t *input) return 1; default: unget_char(c, input); - err = get_freestring(string, input); + err = get_freestring(string, id, input); if (err < 0) return err; return 0; @@ -401,7 +403,7 @@ static int parse_def(snd_config_t *father, input_t *input) #else mode = MERGE; #endif - err = get_string(&id, input); + err = get_string(&id, 1, input); if (err < 0) return err; c = get_nonwhite(input); @@ -472,7 +474,7 @@ static int parse_def(snd_config_t *father, input_t *input) { char *s; unget_char(c, input); - err = get_string(&s, input); + err = get_string(&s, 0, input); if (err < 0) return err; if (!err && ((s[0] >= '0' && s[0] <= '9') || s[0] == '-')) { diff --git a/src/control/control.c b/src/control/control.c index bfd38045..2ed0f38b 100644 --- a/src/control/control.c +++ b/src/control/control.c @@ -43,10 +43,10 @@ int snd_ctl_close(snd_ctl_t *ctl) return res; } -int snd_ctl_file_descriptor(snd_ctl_t *ctl) +int snd_ctl_poll_descriptor(snd_ctl_t *ctl) { assert(ctl); - return ctl->ops->file_descriptor(ctl); + return ctl->ops->poll_descriptor(ctl); } int snd_ctl_hw_info(snd_ctl_t *ctl, snd_ctl_hw_info_t *info) diff --git a/src/control/control_client.c b/src/control/control_client.c index 264f4b3a..ef323e60 100644 --- a/src/control/control_client.c +++ b/src/control/control_client.c @@ -101,7 +101,7 @@ static int snd_ctl_client_shm_close(snd_ctl_t *ctl) return result; } -static int snd_ctl_client_file_descriptor(snd_ctl_t *ctl) +static int snd_ctl_client_poll_descriptor(snd_ctl_t *ctl) { snd_ctl_client_t *client = ctl->private; return client->data_fd; @@ -255,7 +255,7 @@ static int snd_ctl_client_shm_read(snd_ctl_t *ctl, snd_ctl_event_t *event) struct snd_ctl_ops snd_ctl_client_ops = { close: snd_ctl_client_shm_close, - file_descriptor: snd_ctl_client_file_descriptor, + poll_descriptor: snd_ctl_client_poll_descriptor, hw_info: snd_ctl_client_shm_hw_info, clist: snd_ctl_client_shm_clist, cinfo: snd_ctl_client_shm_cinfo, diff --git a/src/control/control_hw.c b/src/control/control_hw.c index 496ce220..8ca8b8b5 100644 --- a/src/control/control_hw.c +++ b/src/control/control_hw.c @@ -48,7 +48,7 @@ static int snd_ctl_hw_close(snd_ctl_t *handle) return res; } -static int snd_ctl_hw_file_descriptor(snd_ctl_t *handle) +static int snd_ctl_hw_poll_descriptor(snd_ctl_t *handle) { snd_ctl_hw_t *hw = handle->private; return hw->fd; @@ -134,7 +134,7 @@ static int snd_ctl_hw_read(snd_ctl_t *handle, snd_ctl_event_t *event) struct snd_ctl_ops snd_ctl_hw_ops = { close: snd_ctl_hw_close, - file_descriptor: snd_ctl_hw_file_descriptor, + poll_descriptor: snd_ctl_hw_poll_descriptor, hw_info: snd_ctl_hw_hw_info, clist: snd_ctl_hw_clist, cinfo: snd_ctl_hw_cinfo, diff --git a/src/control/control_local.h b/src/control/control_local.h index d396df5a..cf1a5828 100644 --- a/src/control/control_local.h +++ b/src/control/control_local.h @@ -25,7 +25,7 @@ struct snd_ctl_ops { int (*close)(snd_ctl_t *handle); - int (*file_descriptor)(snd_ctl_t *handle); + int (*poll_descriptor)(snd_ctl_t *handle); int (*hw_info)(snd_ctl_t *handle, snd_ctl_hw_info_t *info); int (*clist)(snd_ctl_t *handle, snd_control_list_t *list); int (*cinfo)(snd_ctl_t *handle, snd_control_info_t *info); diff --git a/src/hwdep/hwdep.c b/src/hwdep/hwdep.c index dfcbb936..e6440536 100644 --- a/src/hwdep/hwdep.c +++ b/src/hwdep/hwdep.c @@ -86,7 +86,7 @@ int snd_hwdep_close(snd_hwdep_t *hwdep) return res; } -int snd_hwdep_file_descriptor(snd_hwdep_t *hwdep) +int snd_hwdep_poll_descriptor(snd_hwdep_t *hwdep) { if (!hwdep) return -EINVAL; diff --git a/src/mixer/mixer.c b/src/mixer/mixer.c index ed22bf34..b534ffdb 100644 --- a/src/mixer/mixer.c +++ b/src/mixer/mixer.c @@ -77,11 +77,11 @@ int snd_mixer_close(snd_mixer_t *handle) return err; } -int snd_mixer_file_descriptor(snd_mixer_t *handle) +int snd_mixer_poll_descriptor(snd_mixer_t *handle) { if (handle == NULL || handle->ctl_handle == NULL) return -EIO; - return snd_ctl_file_descriptor(handle->ctl_handle); + return snd_ctl_poll_descriptor(handle->ctl_handle); } const char *snd_mixer_simple_channel_name(int channel) diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index f08c8369..82da9063 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -1,15 +1,11 @@ -SUBDIRS = plugin EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plug.c pcm_common.c \ - pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c -libpcm_la_LIBADD = plugin/libpcmplugin.la -noinst_HEADERS = pcm_local.h +libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_linear.c pcm_route.c \ + pcm_mulaw.c pcm_alaw.c pcm_adpcm.c pcm_rate.c pcm_plug.c \ + pcm_misc.c pcm_mmap.c pcm_multi.c pcm_client.c pcm_file.c +noinst_HEADERS = pcm_local.h pcm_plugin.h all: libpcm.la -plugin/libpcmplugin.la: - $(MAKE) -C plugin libpcmplugin.la - INCLUDES=-I$(top_srcdir)/include diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 82a7a218..fbf8bff7 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -25,256 +25,287 @@ #include <errno.h> #include <sys/ioctl.h> #include <sys/poll.h> -#include <sys/uio.h> +#include <dlfcn.h> #include "pcm_local.h" #include "list.h" -snd_pcm_type_t snd_pcm_type(snd_pcm_t *handle) +int snd_pcm_init(snd_pcm_t *pcm) { - assert(handle); - return handle->type; + int err; + err = snd_pcm_mmap_status(pcm, NULL); + if (err < 0) + return err; + err = snd_pcm_mmap_control(pcm, NULL); + if (err < 0) + return err; + return 0; +} + +snd_pcm_type_t snd_pcm_type(snd_pcm_t *pcm) +{ + assert(pcm); + return pcm->type; } -snd_pcm_type_t snd_pcm(snd_pcm_t *handle) +snd_pcm_type_t snd_pcm(snd_pcm_t *pcm) { - assert(handle); - return handle->stream; + assert(pcm); + return pcm->stream; } -int snd_pcm_close(snd_pcm_t *handle) +int snd_pcm_close(snd_pcm_t *pcm) { int ret = 0; int err; - assert(handle); - if (handle->mmap_status) { - if ((err = snd_pcm_munmap_status(handle)) < 0) + assert(pcm); + if (pcm->mmap_status) { + if ((err = snd_pcm_munmap_status(pcm)) < 0) ret = err; } - if (handle->mmap_control) { - if ((err = snd_pcm_munmap_control(handle)) < 0) + if (pcm->mmap_control) { + if ((err = snd_pcm_munmap_control(pcm)) < 0) ret = err; } - if (handle->mmap_data) { - if ((err = snd_pcm_munmap_data(handle)) < 0) + if (pcm->mmap_data) { + if ((err = snd_pcm_munmap_data(pcm)) < 0) ret = err; } - if ((err = handle->ops->close(handle->op_arg)) < 0) + if ((err = pcm->ops->close(pcm->op_arg)) < 0) ret = err; - handle->valid_setup = 0; - free(handle); + pcm->valid_setup = 0; + free(pcm); return ret; } -int snd_pcm_nonblock(snd_pcm_t *handle, int nonblock) +int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock) { int err; - assert(handle); - if ((err = handle->fast_ops->nonblock(handle->fast_op_arg, nonblock)) < 0) + assert(pcm); + if ((err = pcm->ops->nonblock(pcm->fast_op_arg, nonblock)) < 0) return err; if (nonblock) - handle->mode |= SND_PCM_NONBLOCK; + pcm->mode |= SND_PCM_NONBLOCK; else - handle->mode &= ~SND_PCM_NONBLOCK; + pcm->mode &= ~SND_PCM_NONBLOCK; return 0; } -int snd_pcm_info(snd_pcm_t *handle, snd_pcm_info_t *info) +int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) { - assert(handle && info); - return handle->ops->info(handle->op_arg, info); + assert(pcm && info); + return pcm->ops->info(pcm->op_arg, info); } -int snd_pcm_params_info(snd_pcm_t *handle, snd_pcm_params_info_t *info) +int snd_pcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) { - assert(handle && info); - return handle->ops->params_info(handle->op_arg, info); + assert(pcm && info); + return pcm->ops->params_info(pcm->op_arg, info); } -int snd_pcm_setup(snd_pcm_t *handle, snd_pcm_setup_t *setup) +int snd_pcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) { int err; - assert(handle && setup); - if (handle->valid_setup) { - *setup = handle->setup; + assert(pcm && setup); + if (pcm->valid_setup) { + *setup = pcm->setup; return 0; } - if ((err = handle->ops->setup(handle->op_arg, &handle->setup)) < 0) + if ((err = pcm->ops->setup(pcm->op_arg, &pcm->setup)) < 0) return err; - *setup = handle->setup; - handle->bits_per_sample = snd_pcm_format_physical_width(setup->format.format); - handle->bits_per_frame = handle->bits_per_sample * setup->format.channels; - handle->valid_setup = 1; + *setup = pcm->setup; + pcm->bits_per_sample = snd_pcm_format_physical_width(setup->format.sfmt); + pcm->bits_per_frame = pcm->bits_per_sample * setup->format.channels; + pcm->valid_setup = 1; return 0; } -int snd_pcm_channel_info(snd_pcm_t *handle, snd_pcm_channel_info_t *info) +int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { - assert(handle && info); - return handle->fast_ops->channel_info(handle->fast_op_arg, info); + assert(pcm && info); + assert(pcm->valid_setup); + assert(info->channel < pcm->setup.format.channels); + return pcm->ops->channel_info(pcm->op_arg, info); } -int snd_pcm_channel_params(snd_pcm_t *handle, snd_pcm_channel_params_t *params) +int snd_pcm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) { - assert(handle && params); - return handle->fast_ops->channel_params(handle->fast_op_arg, params); + assert(pcm && params); + assert(pcm->valid_setup); + assert(params->channel < pcm->setup.format.channels); + return pcm->ops->channel_params(pcm->op_arg, params); } -int snd_pcm_channel_setup(snd_pcm_t *handle, snd_pcm_channel_setup_t *setup) +int snd_pcm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) { - assert(handle && setup); - assert(handle->valid_setup); - return handle->fast_ops->channel_setup(handle->fast_op_arg, setup); + assert(pcm && setup); + assert(pcm->valid_setup); + assert(setup->channel < pcm->setup.format.channels); + return pcm->ops->channel_setup(pcm->op_arg, setup); } -int snd_pcm_params(snd_pcm_t *handle, snd_pcm_params_t *params) +int snd_pcm_params(snd_pcm_t *pcm, snd_pcm_params_t *params) { int err; snd_pcm_setup_t setup; - assert(handle && params); - assert(!handle->mmap_data); - if ((err = handle->ops->params(handle->op_arg, params)) < 0) + assert(pcm && params); + assert(!pcm->mmap_data); + if ((err = pcm->ops->params(pcm->op_arg, params)) < 0) return err; - handle->valid_setup = 0; - return snd_pcm_setup(handle, &setup); + pcm->valid_setup = 0; + return snd_pcm_setup(pcm, &setup); } -int snd_pcm_status(snd_pcm_t *handle, snd_pcm_status_t *status) +int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status) { - assert(handle && status); - return handle->fast_ops->status(handle->fast_op_arg, status); + assert(pcm && status); + return pcm->fast_ops->status(pcm->fast_op_arg, status); } -int snd_pcm_state(snd_pcm_t *handle) +int snd_pcm_state(snd_pcm_t *pcm) { - assert(handle); - if (handle->mmap_status) - return handle->mmap_status->state; - return handle->fast_ops->state(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->state(pcm->fast_op_arg); } -ssize_t snd_pcm_hw_ptr(snd_pcm_t *handle, int update) +int snd_pcm_delay(snd_pcm_t *pcm, ssize_t *delayp) { - assert(handle); - assert(handle->valid_setup); - if (handle->mmap_status && !update) - return handle->mmap_status->hw_ptr; - return handle->fast_ops->hw_ptr(handle->fast_op_arg, update); + assert(pcm); + return pcm->fast_ops->delay(pcm->fast_op_arg, delayp); } -int snd_pcm_prepare(snd_pcm_t *handle) +int snd_pcm_prepare(snd_pcm_t *pcm) { - assert(handle); - return handle->fast_ops->prepare(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->prepare(pcm->fast_op_arg); } -int snd_pcm_go(snd_pcm_t *handle) +int snd_pcm_start(snd_pcm_t *pcm) { - assert(handle); - return handle->fast_ops->go(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->start(pcm->fast_op_arg); } -int snd_pcm_drain(snd_pcm_t *handle) +int snd_pcm_stop(snd_pcm_t *pcm) { - assert(handle); - return handle->fast_ops->drain(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->stop(pcm->fast_op_arg); } -int snd_pcm_flush(snd_pcm_t *handle) +int snd_pcm_flush(snd_pcm_t *pcm) { - assert(handle); - return handle->fast_ops->flush(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->flush(pcm->fast_op_arg); } -int snd_pcm_pause(snd_pcm_t *handle, int enable) +int snd_pcm_pause(snd_pcm_t *pcm, int enable) { - assert(handle); - return handle->fast_ops->pause(handle->fast_op_arg, enable); + assert(pcm); + return pcm->fast_ops->pause(pcm->fast_op_arg, enable); } -ssize_t snd_pcm_appl_ptr(snd_pcm_t *handle, off_t offset) +ssize_t snd_pcm_appl_ptr(snd_pcm_t *pcm, off_t offset) { - assert(handle); - assert(handle->valid_setup); - if (handle->mmap_control) { + assert(pcm); + assert(pcm->valid_setup); + if (pcm->mmap_control) { if (offset == 0) - return handle->mmap_control->appl_ptr; + return pcm->mmap_control->appl_ptr; } - return handle->fast_ops->appl_ptr(handle->fast_op_arg, offset); + return pcm->fast_ops->appl_ptr(pcm->fast_op_arg, offset); } -ssize_t snd_pcm_write(snd_pcm_t *handle, const void *buffer, size_t size) +ssize_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { - assert(handle); + assert(pcm); assert(size == 0 || buffer); - assert(handle->valid_setup); - assert(size % handle->setup.align == 0); - return handle->fast_ops->write(handle->fast_op_arg, 0, buffer, size); + assert(pcm->valid_setup); + assert(pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED); + assert(!pcm->mmap_data); + return pcm->fast_ops->writei(pcm->fast_op_arg, buffer, size); } -ssize_t snd_pcm_writev(snd_pcm_t *handle, const struct iovec *vector, unsigned long count) +ssize_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, size_t size) { - assert(handle); - assert(count == 0 || vector); - assert(handle->valid_setup); - assert(handle->setup.format.interleave || - count % handle->setup.format.channels == 0); - return handle->fast_ops->writev(handle->fast_op_arg, 0, vector, count); + assert(pcm); + assert(size == 0 || bufs); + assert(pcm->valid_setup); + assert(pcm->setup.xfer_mode == SND_PCM_XFER_NONINTERLEAVED); + assert(!pcm->mmap_data); + return pcm->fast_ops->writen(pcm->fast_op_arg, bufs, size); } -ssize_t snd_pcm_read(snd_pcm_t *handle, void *buffer, size_t size) +ssize_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size) { - assert(handle); + assert(pcm); assert(size == 0 || buffer); - assert(handle->valid_setup); - assert(size % handle->setup.align == 0); - return handle->fast_ops->read(handle->fast_op_arg, 0, buffer, size); + assert(pcm->valid_setup); + assert(pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED); + assert(!pcm->mmap_data); + return pcm->fast_ops->readi(pcm->fast_op_arg, buffer, size); } -ssize_t snd_pcm_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long count) +ssize_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size) { - assert(handle); - assert(count == 0 || vector); - assert(handle->valid_setup); - return handle->fast_ops->readv(handle->fast_op_arg, 0, vector, count); + assert(pcm); + assert(size == 0 || bufs); + assert(pcm->valid_setup); + assert(pcm->setup.xfer_mode == SND_PCM_XFER_NONINTERLEAVED); + assert(!pcm->mmap_data); + return pcm->fast_ops->readn(pcm->fast_op_arg, bufs, size); } -int snd_pcm_link(snd_pcm_t *handle1, snd_pcm_t *handle2) +ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count) { - int fd1, fd2; - switch (handle1->type) { - case SND_PCM_TYPE_HW: - case SND_PCM_TYPE_PLUG: - case SND_PCM_TYPE_MULTI: - fd1 = snd_pcm_file_descriptor(handle1); - break; - default: - errno = -ENOSYS; - return -1; + void **bufs; + int k; + assert(pcm); + assert(pcm->valid_setup); + assert((int)pcm->setup.format.channels == count); + bufs = alloca(sizeof(*bufs) * count); + for (k = 0; k < count; ++k) { + bufs[k] = vector[k].iov_base; + assert(vector[k].iov_len == vector[0].iov_len); } - switch (handle2->type) { - case SND_PCM_TYPE_HW: - case SND_PCM_TYPE_PLUG: - case SND_PCM_TYPE_MULTI: - fd2 = snd_pcm_file_descriptor(handle2); - break; - default: - errno = -ENOSYS; - return -1; + return snd_pcm_writen(pcm, bufs, vector[0].iov_len); +} + +ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count) +{ + void **bufs; + int k; + assert(pcm); + assert(pcm->valid_setup); + assert((int)pcm->setup.format.channels == count); + bufs = alloca(sizeof(*bufs) * count); + for (k = 0; k < count; ++k) { + bufs[k] = vector[k].iov_base; + assert(vector[k].iov_len == vector[0].iov_len); } + return snd_pcm_readn(pcm, bufs, vector[0].iov_len); +} + +/* FIXME */ +#define snd_pcm_link_descriptor snd_pcm_poll_descriptor + +int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2) +{ + int fd1 = snd_pcm_link_descriptor(pcm1); + int fd2 = snd_pcm_link_descriptor(pcm2); + if (fd1 < 0 || fd2 < 0) + return -ENOSYS; if (ioctl(fd1, SND_PCM_IOCTL_LINK, fd2) < 0) return -errno; return 0; } -int snd_pcm_unlink(snd_pcm_t *handle) +int snd_pcm_unlink(snd_pcm_t *pcm) { int fd; - switch (handle->type) { + switch (pcm->type) { case SND_PCM_TYPE_HW: - case SND_PCM_TYPE_PLUG: case SND_PCM_TYPE_MULTI: - fd = snd_pcm_file_descriptor(handle); + fd = snd_pcm_poll_descriptor(pcm); break; default: errno = -ENOSYS; @@ -285,17 +316,17 @@ int snd_pcm_unlink(snd_pcm_t *handle) return 0; } -int snd_pcm_file_descriptor(snd_pcm_t *handle) +int snd_pcm_poll_descriptor(snd_pcm_t *pcm) { - assert(handle); - return handle->fast_ops->file_descriptor(handle->fast_op_arg); + assert(pcm); + return pcm->fast_ops->poll_descriptor(pcm->fast_op_arg); } -int snd_pcm_channels_mask(snd_pcm_t *handle, bitset_t *client_vmask) +int snd_pcm_channels_mask(snd_pcm_t *pcm, bitset_t *cmask) { - assert(handle); - assert(handle->valid_setup); - return handle->fast_ops->channels_mask(handle->fast_op_arg, client_vmask); + assert(pcm); + assert(pcm->valid_setup); + return pcm->fast_ops->channels_mask(pcm->fast_op_arg, cmask); } typedef struct { @@ -306,7 +337,7 @@ typedef struct { static assoc_t *assoc_value(int value, assoc_t *alist) { - while (alist->desc) { + while (alist->name) { if (value == alist->value) return alist; alist++; @@ -333,86 +364,112 @@ static const char *assoc(int value, assoc_t *alist) return "UNKNOWN"; } +#define STATE(v) { SND_PCM_STATE_##v, #v, #v } #define STREAM(v) { SND_PCM_STREAM_##v, #v, #v } -#define MODE(v) { SND_PCM_MODE_##v, #v, #v } -#define FMT(v, d) { SND_PCM_SFMT_##v, #v, d } +#define READY(v) { SND_PCM_READY_##v, #v, #v } #define XRUN(v) { SND_PCM_XRUN_##v, #v, #v } +#define XFER(v) { SND_PCM_XFER_##v, #v, #v } +#define MMAP(v) { SND_PCM_MMAP_##v, #v, #v } +#define SFMT(v, d) { SND_PCM_SFMT_##v, #v, d } +#define XRUN_ACT(v) { SND_PCM_XRUN_ACT_##v, #v, #v } #define START(v) { SND_PCM_START_##v, #v, #v } #define FILL(v) { SND_PCM_FILL_##v, #v, #v } #define END { 0, NULL, NULL } +static assoc_t states[] = { STATE(NOTREADY), STATE(READY), STATE(PREPARED), + STATE(RUNNING), STATE(XRUN), STATE(PAUSED), END }; static assoc_t streams[] = { STREAM(PLAYBACK), STREAM(CAPTURE), END }; -static assoc_t modes[] = { MODE(FRAME), MODE(FRAGMENT), END }; +static assoc_t xruns[] = { XRUN(ASAP), XRUN(FRAGMENT), END }; static assoc_t fmts[] = { - FMT(S8, "Signed 8-bit"), - FMT(U8, "Unsigned 8-bit"), - FMT(S16_LE, "Signed 16-bit Little Endian"), - FMT(S16_BE, "Signed 16-bit Big Endian"), - FMT(U16_LE, "Unsigned 16-bit Little Endian"), - FMT(U16_BE, "Unsigned 16-bit Big Endian"), - FMT(S24_LE, "Signed 24-bit Little Endian"), - FMT(S24_BE, "Signed 24-bit Big Endian"), - FMT(U24_LE, "Unsigned 24-bit Little Endian"), - FMT(U24_BE, "Unsigned 24-bit Big Endian"), - FMT(S32_LE, "Signed 32-bit Little Endian"), - FMT(S32_BE, "Signed 32-bit Big Endian"), - FMT(U32_LE, "Unsigned 32-bit Little Endian"), - FMT(U32_BE, "Unsigned 32-bit Big Endian"), - FMT(FLOAT_LE, "Float Little Endian"), - FMT(FLOAT_BE, "Float Big Endian"), - FMT(FLOAT64_LE, "Float64 Little Endian"), - FMT(FLOAT64_BE, "Float64 Big Endian"), - FMT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), - FMT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), - FMT(MU_LAW, "Mu-Law"), - FMT(A_LAW, "A-Law"), - FMT(IMA_ADPCM, "Ima-ADPCM"), - FMT(MPEG, "MPEG"), - FMT(GSM, "GSM"), - FMT(SPECIAL, "Special"), + SFMT(S8, "Signed 8-bit"), + SFMT(U8, "Unsigned 8-bit"), + SFMT(S16_LE, "Signed 16-bit Little Endian"), + SFMT(S16_BE, "Signed 16-bit Big Endian"), + SFMT(U16_LE, "Unsigned 16-bit Little Endian"), + SFMT(U16_BE, "Unsigned 16-bit Big Endian"), + SFMT(S24_LE, "Signed 24-bit Little Endian"), + SFMT(S24_BE, "Signed 24-bit Big Endian"), + SFMT(U24_LE, "Unsigned 24-bit Little Endian"), + SFMT(U24_BE, "Unsigned 24-bit Big Endian"), + SFMT(S32_LE, "Signed 32-bit Little Endian"), + SFMT(S32_BE, "Signed 32-bit Big Endian"), + SFMT(U32_LE, "Unsigned 32-bit Little Endian"), + SFMT(U32_BE, "Unsigned 32-bit Big Endian"), + SFMT(FLOAT_LE, "Float Little Endian"), + SFMT(FLOAT_BE, "Float Big Endian"), + SFMT(FLOAT64_LE, "Float64 Little Endian"), + SFMT(FLOAT64_BE, "Float64 Big Endian"), + SFMT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), + SFMT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), + SFMT(MU_LAW, "Mu-Law"), + SFMT(A_LAW, "A-Law"), + SFMT(IMA_ADPCM, "Ima-ADPCM"), + SFMT(MPEG, "MPEG"), + SFMT(GSM, "GSM"), + SFMT(SPECIAL, "Special"), END }; -static assoc_t starts[] = { START(GO), START(DATA), START(FULL), END }; -static assoc_t xruns[] = { XRUN(FLUSH), XRUN(DRAIN), END }; -static assoc_t fills[] = { FILL(NONE), FILL(SILENCE_WHOLE), FILL(SILENCE), END }; +static assoc_t starts[] = { START(EXPLICIT), START(DATA), END }; +static assoc_t readys[] = { READY(FRAGMENT), READY(ASAP), END }; +static assoc_t xfers[] = { XFER(INTERLEAVED), XFER(NONINTERLEAVED), END }; +static assoc_t mmaps[] = { MMAP(INTERLEAVED), MMAP(NONINTERLEAVED), END }; +static assoc_t xrun_acts[] = { XRUN_ACT(FLUSH), XRUN_ACT(DRAIN), END }; static assoc_t onoff[] = { {0, "OFF", NULL}, {1, "ON", NULL}, {-1, "ON", NULL}, END }; -int snd_pcm_dump_setup(snd_pcm_t *handle, FILE *fp) +int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp) { snd_pcm_setup_t *setup; - assert(handle); + assert(pcm); assert(fp); - assert(handle->valid_setup); - setup = &handle->setup; - fprintf(fp, "stream: %s\n", assoc(handle->stream, streams)); - fprintf(fp, "mode: %s\n", assoc(setup->mode, modes)); - fprintf(fp, "format: %s\n", assoc(setup->format.format, fmts)); - fprintf(fp, "channels: %d\n", setup->format.channels); - fprintf(fp, "rate: %d (%d/%d=%g)\n", setup->format.rate, setup->rate_master, setup->rate_divisor, (double) setup->rate_master / setup->rate_divisor); + assert(pcm->valid_setup); + setup = &pcm->setup; + fprintf(fp, "stream : %s\n", assoc(pcm->stream, streams)); + fprintf(fp, "format : %s\n", assoc(setup->format.sfmt, fmts)); + fprintf(fp, "channels : %d\n", setup->format.channels); + fprintf(fp, "rate : %d (%d/%d=%g)\n", setup->format.rate, setup->rate_master, setup->rate_divisor, (double) setup->rate_master / setup->rate_divisor); // digital - fprintf(fp, "start_mode: %s\n", assoc(setup->start_mode, starts)); - fprintf(fp, "xrun_mode: %s\n", assoc(setup->xrun_mode, xruns)); - fprintf(fp, "time: %s\n", assoc(setup->time, onoff)); - // ust_time + fprintf(fp, "start_mode : %s\n", assoc(setup->start_mode, starts)); + fprintf(fp, "ready_mode : %s\n", assoc(setup->ready_mode, readys)); + fprintf(fp, "avail_min : %ld\n", (long)setup->avail_min); + fprintf(fp, "xfer_mode : %s\n", assoc(setup->xfer_mode, xfers)); + fprintf(fp, "xfer_min : %ld\n", (long)setup->xfer_min); + fprintf(fp, "xfer_align : %ld\n", (long)setup->xfer_align); + fprintf(fp, "xrun_mode : %s\n", assoc(setup->xrun_mode, xruns)); + fprintf(fp, "xrun_act : %s\n", assoc(setup->xrun_act, xrun_acts)); + fprintf(fp, "xrun_max : %ld\n", (long)setup->xrun_max); + fprintf(fp, "mmap_shape : %s\n", assoc(setup->mmap_shape, mmaps)); fprintf(fp, "buffer_size: %ld\n", (long)setup->buffer_size); - fprintf(fp, "frag_size: %ld\n", (long)setup->frag_size); - fprintf(fp, "frags: %ld\n", (long)setup->frags); - fprintf(fp, "boundary: %ld\n", (long)setup->boundary); - fprintf(fp, "msbits_per_sample: %d\n", setup->msbits_per_sample); - fprintf(fp, "avail_min: %ld\n", (long)setup->avail_min); - fprintf(fp, "align: %ld\n", (long)setup->align); - fprintf(fp, "xrun_max: %ld\n", (long)setup->xrun_max); - fprintf(fp, "fill_mode: %s\n", assoc(setup->fill_mode, fills)); - fprintf(fp, "fill_max: %ld\n", (long)setup->fill_max); + fprintf(fp, "frag_size : %ld\n", (long)setup->frag_size); + fprintf(fp, "boundary : %ld\n", (long)setup->boundary); + fprintf(fp, "time : %s\n", assoc(setup->time, onoff)); + fprintf(fp, "frags : %ld\n", (long)setup->frags); + fprintf(fp, "msbits : %d\n", setup->msbits); + return 0; +} + +int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp) +{ + assert(status); + fprintf(fp, "state : %s\n", assoc(status->state, states)); + fprintf(fp, "trigger_time: %ld.%06ld\n", + status->trigger_time.tv_sec, status->trigger_time.tv_usec); + fprintf(fp, "tstamp : %ld.%06ld\n", + status->tstamp.tv_sec, status->tstamp.tv_usec); + fprintf(fp, "delay : %ld\n", (long)status->delay); + fprintf(fp, "avail_max : %ld\n", (long)status->avail_max); + fprintf(fp, "xruns : %ld\n", (long)status->xruns); + fprintf(fp, "appl_ptr : %ld\n", (long)status->appl_ptr); + fprintf(fp, "hw_ptr : %ld\n", (long)status->hw_ptr); + fprintf(fp, "avail : %ld\n", (long)status->avail); return 0; } -int snd_pcm_dump(snd_pcm_t *handle, FILE *fp) +int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp) { - assert(handle); + assert(pcm); assert(fp); - handle->ops->dump(handle->op_arg, fp); + pcm->ops->dump(pcm->op_arg, fp); return 0; } @@ -440,401 +497,548 @@ int snd_pcm_format_value(const char* name) return -1; } -ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *handle, ssize_t bytes) +ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes) { - assert(handle); - assert(handle->valid_setup); - return bytes * 8 / handle->bits_per_frame; + assert(pcm); + assert(pcm->valid_setup); + return bytes * 8 / pcm->bits_per_frame; } -ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *handle, ssize_t frames) +ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, ssize_t frames) { - assert(handle); - assert(handle->valid_setup); - return frames * handle->bits_per_frame / 8; + assert(pcm); + assert(pcm->valid_setup); + return frames * pcm->bits_per_frame / 8; } -ssize_t snd_pcm_bytes_to_samples(snd_pcm_t *handle, ssize_t bytes) +ssize_t snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes) { - assert(handle); - assert(handle->valid_setup); - return bytes * 8 / handle->bits_per_sample; + assert(pcm); + assert(pcm->valid_setup); + return bytes * 8 / pcm->bits_per_sample; } -ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *handle, ssize_t samples) +ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, ssize_t samples) { - assert(handle); - assert(handle->valid_setup); - return samples * handle->bits_per_sample / 8; + assert(pcm); + assert(pcm->valid_setup); + return samples * pcm->bits_per_sample / 8; } -static int _snd_pcm_open_hw(snd_pcm_t **handlep, snd_config_t *conf, - int stream, int mode) +int snd_pcm_open(snd_pcm_t **pcmp, char *name, + int stream, int mode) { - snd_config_iterator_t i; - long card = -1, device = -1, subdevice = -1; char *str; int err; - snd_config_foreach(i, conf) { + snd_config_t *pcm_conf, *conf, *type_conf; + snd_config_iterator_t i; + char *lib = NULL, *open = NULL; + int (*open_func)(snd_pcm_t **pcmp, char *name, snd_config_t *conf, + int stream, int mode); + void *h; + assert(pcmp && name); + err = snd_config_update(); + if (err < 0) + return err; + err = snd_config_searchv(snd_config, &pcm_conf, "pcm", name, 0); + if (err < 0) + return err; + if (snd_config_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + err = snd_config_search(pcm_conf, "stream", &conf); + if (err >= 0) { + err = snd_config_string_get(conf, &str); + if (err < 0) + return err; + if (strcmp(str, "playback") == 0) { + if (stream != SND_PCM_STREAM_PLAYBACK) + return -EINVAL; + } else if (strcmp(str, "capture") == 0) { + if (stream != SND_PCM_STREAM_CAPTURE) + return -EINVAL; + } else + return -EINVAL; + } + err = snd_config_search(pcm_conf, "type", &conf); + if (err < 0) + return err; + err = snd_config_string_get(conf, &str); + if (err < 0) + return err; + err = snd_config_searchv(snd_config, &type_conf, "pcmtype", str, 0); + if (err < 0) + return err; + snd_config_foreach(i, type_conf) { snd_config_t *n = snd_config_entry(i); if (strcmp(n->id, "comment") == 0) continue; - if (strcmp(n->id, "type") == 0) - continue; - if (strcmp(n->id, "stream") == 0) - continue; - if (strcmp(n->id, "card") == 0) { - err = snd_config_integer_get(n, &card); - if (err < 0) { - err = snd_config_string_get(n, &str); - if (err < 0) - return -EINVAL; - card = snd_card_get_index(str); - if (card < 0) - return card; - } - continue; - } - if (strcmp(n->id, "device") == 0) { - err = snd_config_integer_get(n, &device); + if (strcmp(n->id, "lib") == 0) { + err = snd_config_string_get(n, &lib); if (err < 0) - return err; + return -EINVAL; continue; } - if (strcmp(n->id, "subdevice") == 0) { - err = snd_config_integer_get(n, &subdevice); + if (strcmp(n->id, "open") == 0) { + err = snd_config_string_get(n, &open); if (err < 0) - return err; + return -EINVAL; continue; + return -EINVAL; } - return -EINVAL; } - if (card < 0 || device < 0) + if (!open) return -EINVAL; - return snd_pcm_hw_open_subdevice(handlep, card, device, subdevice, stream, mode); + if (!lib) + lib = "libasound.so"; + h = dlopen(lib, RTLD_NOW); + if (!h) + return -ENOENT; + open_func = dlsym(h, open); + dlclose(h); + if (!open_func) + return -ENXIO; + return open_func(pcmp, name, pcm_conf, stream, mode); } - -static int _snd_pcm_open_plug(snd_pcm_t **handlep, snd_config_t *conf, - int stream, int mode) + +void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + void *buf) { - snd_config_iterator_t i; - char *slave = NULL; - int err; - snd_pcm_t *slave_handle; - snd_config_foreach(i, conf) { - snd_config_t *n = snd_config_entry(i); - if (strcmp(n->id, "comment") == 0) - continue; - if (strcmp(n->id, "type") == 0) - continue; - if (strcmp(n->id, "stream") == 0) - continue; - if (strcmp(n->id, "slave") == 0) { - err = snd_config_string_get(n, &slave); - if (err < 0) - return -EINVAL; - continue; - } - return -EINVAL; + unsigned int channel; + unsigned int channels = pcm->setup.format.channels; + for (channel = 0; channel < channels; ++channel, ++areas) { + areas->addr = buf; + areas->first = channel * pcm->bits_per_sample; + areas->step = pcm->bits_per_frame; } - if (!slave) - return -EINVAL; - /* This is needed cause snd_config_update may destroy config */ - slave = strdup(slave); - if (!slave) - return -ENOMEM; - err = snd_pcm_open(&slave_handle, slave, stream, mode); - free(slave); +} + +void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + void **bufs) +{ + unsigned int channel; + unsigned int channels = pcm->setup.format.channels; + for (channel = 0; channel < channels; ++channel, ++areas, ++bufs) { + areas->addr = *bufs; + areas->first = 0; + areas->step = pcm->bits_per_sample; + } +} + +int snd_pcm_wait(snd_pcm_t *pcm, int timeout) +{ + struct pollfd pfd; + int err; +#if 0 + size_t bavail, aavail; + struct timeval before, after, diff; + bavail = snd_pcm_avail_update(pcm); + gettimeofday(&before, 0); +#endif + pfd.fd = snd_pcm_poll_descriptor(pcm); + pfd.events = pcm->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; + err = poll(&pfd, 1, timeout); if (err < 0) return err; - err = snd_pcm_plug_create(handlep, slave_handle, 1); - if (err < 0) - snd_pcm_close(slave_handle); - return err; +#if 0 + aavail = snd_pcm_avail_update(pcm); + gettimeofday(&after, 0); + timersub(&after, &before, &diff); + fprintf(stderr, "%s %ld.%06ld: get=%d (%d-%d)\n", pcm->stream == SND_PCM_STREAM_PLAYBACK ? "playback" : "capture", diff.tv_sec, diff.tv_usec, aavail - bavail, aavail, bavail); +#endif + return 0; } - -static int _snd_pcm_open_multi(snd_pcm_t **handlep, snd_config_t *conf, - int stream, int mode) + +ssize_t snd_pcm_avail_update(snd_pcm_t *pcm) { - snd_config_iterator_t i, j; - snd_config_t *slave = NULL; - snd_config_t *binding = NULL; - int err; - unsigned int idx; - char **slaves_id = NULL; - char **slaves_name = NULL; - snd_pcm_t **slaves_handle = NULL; - size_t *slaves_channels = NULL; - unsigned int *bindings_cchannel = NULL; - unsigned int *bindings_slave = NULL; - unsigned int *bindings_schannel = NULL; - size_t slaves_count = 0; - size_t bindings_count = 0; - snd_config_foreach(i, conf) { - snd_config_t *n = snd_config_entry(i); - if (strcmp(n->id, "comment") == 0) - continue; - if (strcmp(n->id, "type") == 0) - continue; - if (strcmp(n->id, "stream") == 0) - continue; - if (strcmp(n->id, "slave") == 0) { - if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) - return -EINVAL; - slave = n; - continue; - } - if (strcmp(n->id, "binding") == 0) { - if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) - return -EINVAL; - binding = n; - continue; - } - return -EINVAL; + return pcm->fast_ops->avail_update(pcm->fast_op_arg); +} + +ssize_t snd_pcm_mmap_forward(snd_pcm_t *pcm, size_t size) +{ + assert(size > 0); + return pcm->fast_ops->mmap_forward(pcm->fast_op_arg, size); +} + +size_t snd_pcm_hw_ptr(snd_pcm_t *pcm) +{ + return pcm->mmap_status->hw_ptr; +} + +int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *dst; + unsigned int dst_step; + int width; + u_int64_t silence; + if (!dst_area->addr) + return 0; + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + width = snd_pcm_format_physical_width(format); + silence = snd_pcm_format_silence_64(format); + if (dst_area->step == (unsigned int) width) { + size_t dwords = samples * width / 64; + samples -= dwords * 64 / width; + while (dwords-- > 0) + *((u_int64_t*)dst)++ = silence; + if (samples == 0) + return 0; } - if (!slave || !binding) - return -EINVAL; - snd_config_foreach(i, slave) { - ++slaves_count; - } - snd_config_foreach(i, binding) { - ++bindings_count; - } - slaves_id = calloc(slaves_count, sizeof(*slaves_id)); - slaves_name = calloc(slaves_count, sizeof(*slaves_name)); - slaves_handle = calloc(slaves_count, sizeof(*slaves_handle)); - slaves_channels = calloc(slaves_count, sizeof(*slaves_channels)); - bindings_cchannel = calloc(bindings_count, sizeof(*bindings_cchannel)); - bindings_slave = calloc(bindings_count, sizeof(*bindings_slave)); - bindings_schannel = calloc(bindings_count, sizeof(*bindings_schannel)); - idx = 0; - snd_config_foreach(i, slave) { - snd_config_t *m = snd_config_entry(i); - char *pcm = NULL; - long channels = -1; - slaves_id[idx] = snd_config_id(m); - snd_config_foreach(j, m) { - snd_config_t *n = snd_config_entry(j); - if (strcmp(n->id, "comment") == 0) - continue; - if (strcmp(n->id, "pcm") == 0) { - err = snd_config_string_get(n, &pcm); - if (err < 0) - goto _free; - continue; + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + u_int8_t s0 = silence & 0xf0; + u_int8_t s1 = silence & 0x0f; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + if (dstbit) { + *dst &= 0xf0; + *dst |= s1; + } else { + *dst &= 0x0f; + *dst |= s0; } - if (strcmp(n->id, "channels") == 0) { - err = snd_config_integer_get(n, &channels); - if (err < 0) - goto _free; - continue; + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; } - err = -EINVAL; - goto _free; } - if (!pcm || channels < 0) { - err = -EINVAL; - goto _free; + break; + } + case 8: { + u_int8_t sil = silence; + while (samples-- > 0) { + *dst = sil; + dst += dst_step; } - slaves_name[idx] = strdup(pcm); - slaves_channels[idx] = channels; - ++idx; - } - - idx = 0; - snd_config_foreach(i, binding) { - snd_config_t *m = snd_config_entry(i); - long cchannel = -1, schannel = -1; - int slave = -1; - long val; - char *str; - snd_config_foreach(j, m) { - snd_config_t *n = snd_config_entry(j); - if (strcmp(n->id, "comment") == 0) - continue; - if (strcmp(n->id, "client_channel") == 0) { - err = snd_config_integer_get(n, &cchannel); - if (err < 0) - goto _free; - continue; - } - if (strcmp(n->id, "slave") == 0) { - char buf[32]; - unsigned int k; - err = snd_config_string_get(n, &str); - if (err < 0) { - err = snd_config_integer_get(n, &val); - if (err < 0) - goto _free; - sprintf(buf, "%ld", val); - str = buf; - } - for (k = 0; k < slaves_count; ++k) { - if (strcmp(slaves_id[k], str) == 0) - slave = k; - } - continue; + break; + } + case 16: { + u_int16_t sil = silence; + while (samples-- > 0) { + *(u_int16_t*)dst = sil; + dst += dst_step; + } + break; + } + case 32: { + u_int32_t sil = silence; + while (samples-- > 0) { + *(u_int32_t*)dst = sil; + dst += dst_step; + } + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = silence; + dst += dst_step; + } + break; + } + default: + assert(0); + } + return 0; +} + +int snd_pcm_areas_silence(snd_pcm_channel_area_t *dst_areas, size_t dst_offset, + size_t channels, size_t frames, int format) +{ + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + void *addr = dst_areas->addr; + unsigned int step = dst_areas->step; + snd_pcm_channel_area_t *begin = dst_areas; + int channels1 = channels; + unsigned int chns = 0; + int err; + while (1) { + channels1--; + chns++; + dst_areas++; + if (channels1 == 0 || + dst_areas->addr != addr || + dst_areas->step != step || + dst_areas->first != dst_areas[-1].first + width) + break; + } + if (chns > 1 && chns * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t d; + d.addr = begin->addr; + d.first = begin->first; + d.step = width; + err = snd_pcm_area_silence(&d, dst_offset * chns, frames * chns, format); + channels -= chns; + } else { + err = snd_pcm_area_silence(begin, dst_offset, frames, format); + dst_areas = begin + 1; + channels--; + } + if (err < 0) + return err; + } + return 0; +} + + +int snd_pcm_area_copy(snd_pcm_channel_area_t *src_area, size_t src_offset, + snd_pcm_channel_area_t *dst_area, size_t dst_offset, + size_t samples, int format) +{ + /* FIXME: sub byte resolution and odd dst_offset */ + char *src, *dst; + int width; + int src_step, dst_step; + if (!src_area->addr) + return snd_pcm_area_silence(dst_area, dst_offset, samples, format); + src = snd_pcm_channel_area_addr(src_area, src_offset); + if (!dst_area->addr) + return 0; + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + width = snd_pcm_format_physical_width(format); + if (src_area->step == (unsigned int) width && + dst_area->step == (unsigned int) width) { + size_t bytes = samples * width / 8; + samples -= bytes * 8 / width; + memcpy(dst, src, bytes); + if (samples == 0) + return 0; + } + src_step = src_area->step / 8; + dst_step = dst_area->step / 8; + switch (width) { + case 4: { + int srcbit = src_area->first % 8; + int srcbit_step = src_area->step % 8; + int dstbit = dst_area->first % 8; + int dstbit_step = dst_area->step % 8; + while (samples-- > 0) { + unsigned char srcval; + if (srcbit) + srcval = *src & 0x0f; + else + srcval = *src & 0xf0; + if (dstbit) + *dst &= 0xf0; + else + *dst &= 0x0f; + *dst |= srcval; + src += src_step; + srcbit += srcbit_step; + if (srcbit == 8) { + src++; + srcbit = 0; } - if (strcmp(n->id, "slave_channel") == 0) { - err = snd_config_integer_get(n, &schannel); - if (err < 0) - goto _free; - continue; + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; } - err = -EINVAL; - goto _free; } - if (cchannel < 0 || slave < 0 || schannel < 0) { - err = -EINVAL; - goto _free; + break; + } + case 8: { + while (samples-- > 0) { + *dst = *src; + src += src_step; + dst += dst_step; } - if ((size_t)slave >= slaves_count) { - err = -EINVAL; - goto _free; + break; + } + case 16: { + while (samples-- > 0) { + *(u_int16_t*)dst = *(u_int16_t*)src; + src += src_step; + dst += dst_step; } - if ((unsigned int) schannel >= slaves_channels[slave]) { - err = -EINVAL; - goto _free; + break; + } + case 32: { + while (samples-- > 0) { + *(u_int32_t*)dst = *(u_int32_t*)src; + src += src_step; + dst += dst_step; } - bindings_cchannel[idx] = cchannel; - bindings_slave[idx] = slave; - bindings_schannel[idx] = schannel; - ++idx; - } - - for (idx = 0; idx < slaves_count; ++idx) { - err = snd_pcm_open(&slaves_handle[idx], slaves_name[idx], stream, mode); - if (err < 0) - goto _free; - } - err = snd_pcm_multi_create(handlep, slaves_count, slaves_handle, - slaves_channels, - bindings_count, bindings_cchannel, - bindings_slave, bindings_schannel, - 1); -_free: - if (err < 0) { - for (idx = 0; idx < slaves_count; ++idx) { - if (slaves_handle[idx]) - snd_pcm_close(slaves_handle[idx]); - if (slaves_name[idx]) - free(slaves_name[idx]); + break; + } + case 64: { + while (samples-- > 0) { + *(u_int64_t*)dst = *(u_int64_t*)src; + src += src_step; + dst += dst_step; } + break; } - if (slaves_name) - free(slaves_name); - if (slaves_handle) - free(slaves_handle); - if (slaves_channels) - free(slaves_channels); - if (bindings_cchannel) - free(bindings_cchannel); - if (bindings_slave) - free(bindings_slave); - if (bindings_schannel) - free(bindings_schannel); - return err; + default: + assert(0); + } + return 0; } -static int _snd_pcm_open_client(snd_pcm_t **handlep, snd_config_t *conf, - int stream, int mode) +int snd_pcm_areas_copy(snd_pcm_channel_area_t *src_areas, size_t src_offset, + snd_pcm_channel_area_t *dst_areas, size_t dst_offset, + size_t channels, size_t frames, int format) { - snd_config_iterator_t i; - char *socket = NULL; - char *name = NULL; - char *host = NULL; - long port = -1; - int err; - snd_config_foreach(i, conf) { - snd_config_t *n = snd_config_entry(i); - if (strcmp(n->id, "comment") == 0) - continue; - if (strcmp(n->id, "type") == 0) - continue; - if (strcmp(n->id, "stream") == 0) - continue; - if (strcmp(n->id, "socket") == 0) { - err = snd_config_string_get(n, &socket); - if (err < 0) - return -EINVAL; - continue; + int width = snd_pcm_format_physical_width(format); + while (channels > 0) { + unsigned int step = src_areas->step; + void *src_addr = src_areas->addr; + snd_pcm_channel_area_t *src_start = src_areas; + void *dst_addr = dst_areas->addr; + snd_pcm_channel_area_t *dst_start = dst_areas; + int channels1 = channels; + unsigned int chns = 0; + while (dst_areas->step == step) { + channels1--; + chns++; + src_areas++; + dst_areas++; + if (channels1 == 0 || + src_areas->step != step || + src_areas->addr != src_addr || + dst_areas->addr != dst_addr || + src_areas->first != src_areas[-1].first + width || + dst_areas->first != dst_areas[-1].first + width) + break; } - if (strcmp(n->id, "host") == 0) { - err = snd_config_string_get(n, &host); - if (err < 0) - return -EINVAL; - continue; + if (chns > 1 && chns * width == step) { + /* Collapse the areas */ + snd_pcm_channel_area_t s, d; + s.addr = src_start->addr; + s.first = src_start->first; + s.step = width; + d.addr = dst_start->addr; + d.first = dst_start->first; + d.step = width; + snd_pcm_area_copy(&s, src_offset * chns, &d, dst_offset * chns, frames * chns, format); + channels -= chns; + } else { + snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format); + src_areas = src_start + 1; + dst_areas = dst_start + 1; + channels--; } - if (strcmp(n->id, "port") == 0) { - err = snd_config_integer_get(n, &port); - if (err < 0) - return -EINVAL; - continue; + } + return 0; +} + +ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + size_t offset, size_t size, + snd_pcm_xfer_areas_func_t func) +{ + size_t xfer = 0; + ssize_t err = 0; + int state = snd_pcm_state(pcm); + assert(size > 0); + assert(state >= SND_PCM_STATE_PREPARED); + if (state == SND_PCM_STATE_PREPARED && + pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + err = snd_pcm_start(pcm); + if (err < 0) + return err; + state = SND_PCM_STATE_RUNNING; + } + while (xfer < size) { + ssize_t avail; + size_t frames; + again: + avail = snd_pcm_avail_update(pcm); + if (avail < 0) { + err = avail; + break; } - if (strcmp(n->id, "name") == 0) { - err = snd_config_string_get(n, &name); + if ((size_t)avail < pcm->setup.avail_min) { + if (state != SND_PCM_STATE_RUNNING) { + err = -EPIPE; + break; + } + if (pcm->mode & SND_PCM_NONBLOCK) { + err = -EAGAIN; + break; + } + err = snd_pcm_wait(pcm, -1); if (err < 0) - return -EINVAL; - continue; + break; + state = snd_pcm_state(pcm); + goto again; } - return -EINVAL; - } - if (!name) - return -EINVAL; - if (socket) { - if (port >= 0 || host) - return -EINVAL; - return snd_pcm_client_create(handlep, socket, -1, SND_TRANSPORT_TYPE_SHM, name, stream, mode); - } else { - if (port < 0 || !name) - return -EINVAL; - return snd_pcm_client_create(handlep, host, port, SND_TRANSPORT_TYPE_TCP, name, stream, mode); + frames = size - xfer; + if (frames > (size_t)avail) + frames = avail; + err = func(pcm, areas, offset, frames, 0); + if (err < 0) + break; + assert((size_t)err == frames); + xfer += err; + offset += err; } + if (xfer > 0) + return xfer; + return err; } - -int snd_pcm_open(snd_pcm_t **handlep, char *name, - int stream, int mode) + +ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + size_t offset, size_t size, + snd_pcm_xfer_areas_func_t func) { - char *str; - int err; - snd_config_t *pcm_conf, *conf; - assert(handlep && name); - err = snd_config_update(); - if (err < 0) - return err; - err = snd_config_searchv(snd_config, &pcm_conf, "pcm", name, 0); - if (err < 0) - return err; - if (snd_config_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) - return -EINVAL; - err = snd_config_search(pcm_conf, "stream", &conf); - if (err >= 0) { - err = snd_config_string_get(conf, &str); + size_t xfer = 0; + ssize_t err = 0; + int state = snd_pcm_state(pcm); + assert(size > 0); + assert(state >= SND_PCM_STATE_PREPARED); + while (xfer < size) { + ssize_t avail; + size_t frames; + again: + if (state == SND_PCM_STATE_XRUN) { + err = -EPIPE; + break; + } + avail = snd_pcm_avail_update(pcm); + if (avail < 0) { + err = avail; + break; + } + if ((size_t)avail < pcm->setup.avail_min) { + if (state != SND_PCM_STATE_RUNNING) { + err = -EPIPE; + break; + } + if (pcm->mode & SND_PCM_NONBLOCK) { + err = -EAGAIN; + break; + } + err = snd_pcm_wait(pcm, -1); + if (err < 0) + break; + state = snd_pcm_state(pcm); + goto again; + } + frames = size - xfer; + if (frames > (size_t)avail) + frames = avail; + err = func(pcm, areas, offset, frames, 0); if (err < 0) - return err; - if (strcmp(str, "playback") == 0) { - if (stream != SND_PCM_STREAM_PLAYBACK) - return -EINVAL; - } else if (strcmp(str, "capture") == 0) { - if (stream != SND_PCM_STREAM_CAPTURE) - return -EINVAL; - } else - return -EINVAL; + break; + assert((size_t)err == frames); + xfer += err; + offset += err; + if (state == SND_PCM_STATE_PREPARED && + pcm->setup.start_mode != SND_PCM_START_EXPLICIT) { + err = snd_pcm_start(pcm); + if (err < 0) + break; + } } - err = snd_config_search(pcm_conf, "type", &conf); - if (err < 0) - return err; - err = snd_config_string_get(conf, &str); - if (err < 0) - return err; - if (strcmp(str, "hw") == 0) - return _snd_pcm_open_hw(handlep, pcm_conf, stream, mode); - else if (strcmp(str, "plug") == 0) - return _snd_pcm_open_plug(handlep, pcm_conf, stream, mode); - else if (strcmp(str, "multi") == 0) - return _snd_pcm_open_multi(handlep, pcm_conf, stream, mode); - else if (strcmp(str, "client") == 0) - return _snd_pcm_open_client(handlep, pcm_conf, stream, mode); - else - return -EINVAL; + if (xfer > 0) + return xfer; + return err; } + diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c new file mode 100644 index 00000000..f02fbae3 --- /dev/null +++ b/src/pcm/pcm_adpcm.c @@ -0,0 +1,652 @@ +/* + * PCM - Ima-ADPC conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si> + * Jaroslav Kysela <perex@suse.cz> + * + * Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code + * by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992 + * by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. + * + * 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. + * + */ + +/* +These routines convert 16 bit linear PCM samples to 4 bit ADPCM code +and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which +is being recommended by the IMA Digital Audio Technical Working Group. + +The algorithm for this coder was taken from: +Proposal for Standardized Audio Interstreamge Formats, +IMA compatability project proceedings, Vol 2, Issue 2, May 1992. + +- No, this is *not* a G.721 coder/decoder. The algorithm used by G.721 + is very complicated, requiring oodles of floating-point ops per + sample (resulting in very poor performance). I have not done any + tests myself but various people have assured my that 721 quality is + actually lower than DVI quality. + +- No, it probably isn't a RIFF ADPCM decoder either. Trying to decode + RIFF ADPCM with these routines seems to result in something + recognizable but very distorted. + +- No, it is not a CDROM-XA coder either, as far as I know. I haven't + come across a good description of XA yet. + */ + +#include <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef struct { + int pred_val; /* Calculated predicted value */ + int step_idx; /* Previous StepSize lookup index */ +} adpcm_state_t; + +typedef void (*adpcm_f)(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getputidx, + adpcm_state_t *states); + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int getput_idx; + adpcm_f func; + int sformat; + int cformat; + int cxfer_mode, cmmap_shape; + adpcm_state_t *states; +} snd_pcm_adpcm_t; + +/* First table lookup for Ima-ADPCM quantizer */ +static char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; + +/* Second table lookup for Ima-ADPCM quantizer */ +static short StepSize[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 +}; + +static char adpcm_encoder(int sl, adpcm_state_t * state) +{ + short diff; /* Difference between sl and predicted sample */ + short pred_diff; /* Predicted difference to next sample */ + + unsigned char sign; /* sign of diff */ + short step; /* holds previous StepSize value */ + unsigned char adjust_idx; /* Index to IndexAdjust lookup table */ + + int i; + + /* Compute difference to previous predicted value */ + diff = sl - state->pred_val; + sign = (diff < 0) ? 0x8 : 0x0; + if (sign) { + diff = -diff; + } + + /* + * This code *approximately* computes: + * adjust_idx = diff * 4 / step; + * pred_diff = (adjust_idx + 0.5) * step / 4; + * + * But in shift step bits are dropped. The net result of this is + * that even if you have fast mul/div hardware you cannot put it to + * good use since the fixup would be too expensive. + */ + + step = StepSize[state->step_idx]; + + /* Divide and clamp */ + pred_diff = step >> 3; + for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) { + if (diff >= step) { + adjust_idx |= i; + diff -= step; + pred_diff += step; + } + } + + /* Update and clamp previous predicted value */ + state->pred_val += sign ? -pred_diff : pred_diff; + + if (state->pred_val > 32767) { + state->pred_val = 32767; + } else if (state->pred_val < -32768) { + state->pred_val = -32768; + } + + /* Update and clamp StepSize lookup table index */ + state->step_idx += IndexAdjust[adjust_idx]; + + if (state->step_idx < 0) { + state->step_idx = 0; + } else if (state->step_idx > 88) { + state->step_idx = 88; + } + return (sign | adjust_idx); +} + + +static int adpcm_decoder(unsigned char code, adpcm_state_t * state) +{ + short pred_diff; /* Predicted difference to next sample */ + short step; /* holds previous StepSize value */ + char sign; + + int i; + + /* Separate sign and magnitude */ + sign = code & 0x8; + code &= 0x7; + + /* + * Computes pred_diff = (code + 0.5) * step / 4, + * but see comment in adpcm_coder. + */ + + step = StepSize[state->step_idx]; + + /* Compute difference and new predicted value */ + pred_diff = step >> 3; + for (i = 0x4; i; i >>= 1, step >>= 1) { + if (code & i) { + pred_diff += step; + } + } + state->pred_val += (sign) ? -pred_diff : pred_diff; + + /* Clamp output value */ + if (state->pred_val > 32767) { + state->pred_val = 32767; + } else if (state->pred_val < -32768) { + state->pred_val = -32768; + } + + /* Find new StepSize index value */ + state->step_idx += IndexAdjust[code]; + + if (state->step_idx < 0) { + state->step_idx = 0; + } else if (state->step_idx > 88) { + state->step_idx = 88; + } + return (state->pred_val); +} + +static void adpcm_decode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int putidx, + adpcm_state_t *states) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + void *put = put_s16_labels[putidx]; + size_t channel; + for (channel = 0; channel < channels; ++channel, ++states) { + char *src; + int srcbit; + char *dst; + int src_step, srcbit_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + srcbit = src_area->first + src_area->step * src_offset; + src = src_area->addr + srcbit / 8; + srcbit %= 8; + src_step = src_area->step / 8; + srcbit_step = src_area->step % 8; + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + int16_t sample; + int v; + if (srcbit) + v = *src & 0x0f; + else + v = (*src >> 4) & 0x0f; + sample = adpcm_decoder(v, states); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + srcbit += srcbit_step; + if (srcbit == 8) { + src++; + srcbit = 0; + } + dst += dst_step; + } + } +} + +static void adpcm_encode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getidx, + adpcm_state_t *states) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + void *get = get_s16_labels[getidx]; + size_t channel; + int16_t sample = 0; + for (channel = 0; channel < channels; ++channel, ++states) { + char *src; + char *dst; + int dstbit; + int src_step, dst_step, dstbit_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + src_step = snd_pcm_channel_area_step(src_area); + dstbit = dst_area->first + dst_area->step * dst_offset; + dst = dst_area->addr + dstbit / 8; + dstbit %= 8; + dst_step = dst_area->step / 8; + dstbit_step = dst_area->step % 8; + frames1 = frames; + while (frames1-- > 0) { + int v; + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + v = adpcm_encoder(sample, states); + if (dstbit) + *dst = (*dst & 0xf0) | v; + else + *dst = (*dst & 0x0f) | (v << 4); + src += src_step; + dst += dst_step; + dstbit += dstbit_step; + if (dstbit == 8) { + dst++; + dstbit = 0; + } + } + } +} + +static int snd_pcm_adpcm_close(snd_pcm_t *pcm) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + int err = 0; + if (adpcm->plug.close_slave) + err = snd_pcm_close(adpcm->plug.slave); + if (adpcm->states) + free(adpcm->states); + free(adpcm); + return 0; +} + +static int snd_pcm_adpcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT) { + if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? + !snd_pcm_format_linear(sfmt) : + sfmt != SND_PCM_SFMT_IMA_ADPCM) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + } + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + info->req.format.sfmt = adpcm->sformat; + err = snd_pcm_params_info(adpcm->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? + SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_IMA_ADPCM; + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return err; +} + +static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + snd_pcm_t *slave = adpcm->plug.slave; + int err; + if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ? + !snd_pcm_format_linear(params->format.sfmt) : + params->format.sfmt != SND_PCM_SFMT_IMA_ADPCM) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + adpcm->cformat = params->format.sfmt; + adpcm->cxfer_mode = params->xfer_mode; + adpcm->cmmap_shape = params->mmap_shape; + params->format.sfmt = adpcm->sformat; + params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; + params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; + err = snd_pcm_params(slave, params); + params->format.sfmt = adpcm->cformat; + params->xfer_mode = adpcm->cxfer_mode; + params->mmap_shape = adpcm->cmmap_shape; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_adpcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + int err = snd_pcm_setup(adpcm->plug.slave, setup); + if (err < 0) + return err; + assert(adpcm->sformat == setup->format.sfmt); + if (adpcm->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = adpcm->cxfer_mode; + if (adpcm->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = adpcm->cmmap_shape; + setup->format.sfmt = adpcm->cformat; + setup->mmap_bytes = 0; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) { + adpcm->getput_idx = getput_index(adpcm->cformat); + adpcm->func = adpcm_encode; + } else { + adpcm->getput_idx = getput_index(adpcm->sformat); + adpcm->func = adpcm_decode; + } + } else { + if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) { + adpcm->getput_idx = getput_index(adpcm->cformat); + adpcm->func = adpcm_decode; + } else { + adpcm->getput_idx = getput_index(adpcm->sformat); + adpcm->func = adpcm_encode; + } + } + if (adpcm->states) + free(adpcm->states); + adpcm->states = malloc(setup->format.channels * sizeof(*adpcm->states)); + return 0; +} + +static int snd_pcm_adpcm_init(snd_pcm_t *pcm) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + unsigned int k; + for (k = 0; k < pcm->setup.format.channels; ++k) { + adpcm->states[k].pred_val = 0; + adpcm->states[k].step_idx = 0; + } + return 0; +} + +static ssize_t snd_pcm_adpcm_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + snd_pcm_t *slave = adpcm->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + adpcm->func(areas, offset, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + frames, pcm->setup.format.channels, + adpcm->getput_idx, adpcm->states); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static ssize_t snd_pcm_adpcm_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + snd_pcm_t *slave = adpcm->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + adpcm->func(slave->mmap_areas, snd_pcm_mmap_offset(slave), + areas, offset, + frames, pcm->setup.format.channels, + adpcm->getput_idx, adpcm->states); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_adpcm_t *adpcm = pcm->private; + fprintf(fp, "Ima-ADPCM conversion PCM (%s)\n", + snd_pcm_format_name(adpcm->sformat)); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(adpcm->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_adpcm_ops = { + close: snd_pcm_adpcm_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_adpcm_params_info, + params: snd_pcm_adpcm_params, + setup: snd_pcm_adpcm_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_plugin_channel_setup, + dump: snd_pcm_adpcm_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int snd_pcm_adpcm_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_adpcm_t *adpcm; + int err; + assert(handlep && slave); + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_IMA_ADPCM) + return -EINVAL; + adpcm = calloc(1, sizeof(snd_pcm_adpcm_t)); + if (!adpcm) { + return -ENOMEM; + } + adpcm->sformat = sformat; + adpcm->plug.read = snd_pcm_adpcm_read_areas; + adpcm->plug.write = snd_pcm_adpcm_write_areas; + adpcm->plug.init = snd_pcm_adpcm_init; + adpcm->plug.slave = slave; + adpcm->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(adpcm); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_ADPCM; + handle->stream = slave->stream; + handle->ops = &snd_pcm_adpcm_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = adpcm; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_IMA_ADPCM) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname || !sformat) + return -EINVAL; + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_adpcm_open(pcmp, sformat, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c new file mode 100644 index 00000000..570de595 --- /dev/null +++ b/src/pcm/pcm_alaw.c @@ -0,0 +1,519 @@ +/* + * PCM - A-Law conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef void (*alaw_f)(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getputidx); + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int getput_idx; + alaw_f func; + int sformat; + int cformat; + int cxfer_mode, cmmap_shape; +} snd_pcm_alaw_t; + +static inline int val_seg(int val) +{ + int r = 1; + val >>= 8; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law + * + * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. + * + * Linear Input Code Compressed Code + * ------------------------ --------------- + * 0000000wxyza 000wxyz + * 0000001wxyza 001wxyz + * 000001wxyzab 010wxyz + * 00001wxyzabc 011wxyz + * 0001wxyzabcd 100wxyz + * 001wxyzabcde 101wxyz + * 01wxyzabcdef 110wxyz + * 1wxyzabcdefg 111wxyz + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static unsigned char s16_to_alaw(int pcm_val) +{ + int mask; + int seg; + unsigned char aval; + + if (pcm_val >= 0) { + mask = 0xD5; + } else { + mask = 0x55; + pcm_val = -pcm_val; + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + } + + if (pcm_val < 256) + aval = pcm_val >> 4; + else { + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + } + return aval ^ mask; +} + +/* + * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM + * + */ +static int alaw_to_s16(unsigned char a_val) +{ + int t; + int seg; + + a_val ^= 0x55; + t = a_val & 0x7f; + if (t < 16) + t = (t << 4) + 8; + else { + seg = (t >> 4) & 0x07; + t = ((t & 0x0f) << 4) + 0x108; + t <<= seg -1; + } + return ((a_val & 0x80) ? t : -t); +} + +static void alaw_decode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int putidx) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + void *put = put_s16_labels[putidx]; + size_t channel; + for (channel = 0; channel < channels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + int16_t sample = alaw_to_s16(*src); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static void alaw_encode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getidx) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + void *get = get_s16_labels[getidx]; + size_t channel; + int16_t sample = 0; + for (channel = 0; channel < channels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + *dst = s16_to_alaw(sample); + src += src_step; + dst += dst_step; + } + } +} + +static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_alaw_t *alaw = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT) { + if (alaw->sformat == SND_PCM_SFMT_A_LAW ? + !snd_pcm_format_linear(sfmt) : + sfmt != SND_PCM_SFMT_A_LAW) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + } + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + info->req.format.sfmt = alaw->sformat; + err = snd_pcm_params_info(alaw->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ? + SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW; + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return err; +} + +static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_alaw_t *alaw = pcm->private; + snd_pcm_t *slave = alaw->plug.slave; + int err; + if (alaw->sformat == SND_PCM_SFMT_A_LAW ? + !snd_pcm_format_linear(params->format.sfmt) : + params->format.sfmt != SND_PCM_SFMT_A_LAW) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + alaw->cformat = params->format.sfmt; + alaw->cxfer_mode = params->xfer_mode; + alaw->cmmap_shape = params->mmap_shape; + params->format.sfmt = alaw->sformat; + params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; + params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; + err = snd_pcm_params(slave, params); + params->format.sfmt = alaw->cformat; + params->xfer_mode = alaw->cxfer_mode; + params->mmap_shape = alaw->cmmap_shape; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_alaw_t *alaw = pcm->private; + int err = snd_pcm_setup(alaw->plug.slave, setup); + if (err < 0) + return err; + assert(alaw->sformat == setup->format.sfmt); + if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = alaw->cxfer_mode; + if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = alaw->cmmap_shape; + setup->format.sfmt = alaw->cformat; + setup->mmap_bytes = 0; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + if (alaw->sformat == SND_PCM_SFMT_A_LAW) { + alaw->getput_idx = getput_index(alaw->cformat); + alaw->func = alaw_encode; + } else { + alaw->getput_idx = getput_index(alaw->sformat); + alaw->func = alaw_decode; + } + } else { + if (alaw->sformat == SND_PCM_SFMT_A_LAW) { + alaw->getput_idx = getput_index(alaw->cformat); + alaw->func = alaw_decode; + } else { + alaw->getput_idx = getput_index(alaw->sformat); + alaw->func = alaw_encode; + } + } + return 0; +} + +static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_alaw_t *alaw = pcm->private; + snd_pcm_t *slave = alaw->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + alaw->func(areas, offset, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + frames, pcm->setup.format.channels, + alaw->getput_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_alaw_t *alaw = pcm->private; + snd_pcm_t *slave = alaw->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + alaw->func(slave->mmap_areas, snd_pcm_mmap_offset(slave), + areas, offset, + frames, pcm->setup.format.channels, + alaw->getput_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_alaw_t *alaw = pcm->private; + fprintf(fp, "A-Law conversion PCM (%s)\n", + snd_pcm_format_name(alaw->sformat)); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(alaw->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_alaw_ops = { + close: snd_pcm_plugin_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_alaw_params_info, + params: snd_pcm_alaw_params, + setup: snd_pcm_alaw_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_plugin_channel_setup, + dump: snd_pcm_alaw_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_alaw_t *alaw; + int err; + assert(handlep && slave); + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_A_LAW) + return -EINVAL; + alaw = calloc(1, sizeof(snd_pcm_alaw_t)); + if (!alaw) { + return -ENOMEM; + } + alaw->sformat = sformat; + alaw->plug.read = snd_pcm_alaw_read_areas; + alaw->plug.write = snd_pcm_alaw_write_areas; + alaw->plug.slave = slave; + alaw->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(alaw); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_ALAW; + handle->stream = slave->stream; + handle->ops = &snd_pcm_alaw_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = alaw; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_A_LAW) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname || !sformat) + return -EINVAL; + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_alaw_open(pcmp, sformat, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/pcm_client.c b/src/pcm/pcm_client.c index d0b865bc..7e7799ab 100644 --- a/src/pcm/pcm_client.c +++ b/src/pcm/pcm_client.c @@ -291,221 +291,116 @@ static int snd_pcm_client_shm_status(snd_pcm_t *pcm, snd_pcm_status_t * status) static int snd_pcm_client_shm_state(snd_pcm_t *pcm) { - snd_pcm_status_t status; - int err = snd_pcm_client_shm_status(pcm, &status); - if (err < 0) - return err; - return status.state; -} - -static ssize_t snd_pcm_client_shm_hw_ptr(snd_pcm_t *pcm, int update) -{ - snd_pcm_client_t *client = pcm->private; - snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - int err; - ctrl->cmd = SND_PCM_IOCTL_HW_PTR; - ctrl->u.hw_ptr = update; - err = snd_pcm_client_shm_action(pcm); - if (err < 0) - return err; - return ctrl->result; -} - -static int snd_pcm_client_shm_prepare(snd_pcm_t *pcm) -{ snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_PREPARE; + ctrl->cmd = SND_PCM_IOCTL_STATE; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static int snd_pcm_client_shm_go(snd_pcm_t *pcm) +static int snd_pcm_client_shm_delay(snd_pcm_t *pcm, ssize_t *delayp) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_GO; + ctrl->cmd = SND_PCM_IOCTL_DELAY; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; + *delayp = ctrl->u.delay; return ctrl->result; } -static int snd_pcm_client_shm_drain(snd_pcm_t *pcm) +static ssize_t snd_pcm_client_avail_update(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_DRAIN; + ctrl->cmd = SND_PCM_IOCTL_AVAIL_UPDATE; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static int snd_pcm_client_shm_flush(snd_pcm_t *pcm) -{ - snd_pcm_client_t *client = pcm->private; - snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - int err; - ctrl->cmd = SND_PCM_IOCTL_FLUSH; - err = snd_pcm_client_shm_action(pcm); - if (err < 0) - return err; - return ctrl->result; -} - -static int snd_pcm_client_shm_pause(snd_pcm_t *pcm, int enable) +static int snd_pcm_client_shm_prepare(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_PAUSE; - ctrl->u.pause = enable; + ctrl->cmd = SND_PCM_IOCTL_PREPARE; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static ssize_t snd_pcm_client_shm_appl_ptr(snd_pcm_t *pcm, off_t offset) +static int snd_pcm_client_shm_start(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; - ctrl->cmd = SND_PCM_IOCTL_APPL_PTR; - ctrl->u.appl_ptr = offset; + ctrl->cmd = SND_PCM_IOCTL_START; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static ssize_t snd_pcm_client_shm_write(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size) +static int snd_pcm_client_shm_stop(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - size_t maxsize = PCM_SHM_DATA_MAXLEN; - size_t bytes = snd_pcm_frames_to_bytes(pcm, size); int err; - if (bytes > maxsize) - return -EINVAL; - ctrl->cmd = SND_PCM_IOCTL_WRITE_FRAMES; -// ctrl->u.write.tstamp = *tstamp; - ctrl->u.write.count = size; - memcpy(ctrl->data, buffer, bytes); + ctrl->cmd = SND_PCM_IOCTL_STOP; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static ssize_t snd_pcm_client_shm_writev(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count) +static int snd_pcm_client_shm_flush(snd_pcm_t *pcm) { - /* FIXME: interleaved */ snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - size_t vecsize = count * sizeof(struct iovec); - size_t maxsize = PCM_SHM_DATA_MAXLEN; - int bits_per_sample = pcm->bits_per_sample; - char *base; - struct iovec *vec; - unsigned long k; - size_t ofs; int err; - if (vecsize > maxsize) - return -EINVAL; - maxsize -= vecsize; - ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES; -// ctrl->u.writev.tstamp = *tstamp; - ctrl->u.writev.count = count; - memcpy(ctrl->data, vector, vecsize); - vec = (struct iovec *) ctrl->data; - base = ctrl->data + vecsize; - ofs = 0; - for (k = 0; k < count; ++k) { - size_t len = vector[k].iov_len * bits_per_sample / 8; - memcpy(base + ofs, vector[k].iov_base, len); - vec[k].iov_base = (void *) ofs; - ofs += len; - } + ctrl->cmd = SND_PCM_IOCTL_FLUSH; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; return ctrl->result; } -static ssize_t snd_pcm_client_shm_read(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size) +static int snd_pcm_client_shm_pause(snd_pcm_t *pcm, int enable) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - size_t maxsize = PCM_SHM_DATA_MAXLEN; - size_t bytes = snd_pcm_frames_to_bytes(pcm, size); int err; - if (bytes > maxsize) - return -EINVAL; - ctrl->cmd = SND_PCM_IOCTL_READ_FRAMES; -// ctrl->u.read.tstamp = *tstamp; - ctrl->u.read.count = size; + ctrl->cmd = SND_PCM_IOCTL_PAUSE; + ctrl->u.pause = enable; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; - if (ctrl->result <= 0) - return ctrl->result; - bytes = snd_pcm_frames_to_bytes(pcm, ctrl->result); - memcpy(buffer, ctrl->data, bytes); return ctrl->result; } -ssize_t snd_pcm_client_shm_readv(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count) +static ssize_t snd_pcm_client_shm_appl_ptr(snd_pcm_t *pcm, off_t offset) { - /* FIXME: interleaved */ snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; - size_t vecsize = count * sizeof(struct iovec); - size_t maxsize = PCM_SHM_DATA_MAXLEN; - int bits_per_sample = pcm->bits_per_sample; - char *base; - struct iovec *vec; - unsigned long k; - size_t ofs, bytes; int err; - if (vecsize > maxsize) - return -EINVAL; - maxsize -= vecsize; - ctrl->cmd = SND_PCM_IOCTL_WRITEV_FRAMES; -// ctrl->u.writev.tstamp = *tstamp; - ctrl->u.writev.count = count; - memcpy(ctrl->data, vector, vecsize); - vec = (struct iovec *) ctrl->data; - base = ctrl->data + vecsize; - ofs = 0; - for (k = 0; k < count; ++k) { - size_t len = vector[k].iov_len * bits_per_sample / 8; - vec[k].iov_base = (void *) ofs; - ofs += len; - } + ctrl->cmd = SND_PCM_IOCTL_APPL_PTR; + ctrl->u.appl_ptr = offset; err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; - if (ctrl->result <= 0) - return ctrl->result; - bytes = snd_pcm_frames_to_bytes(pcm, ctrl->result); - ofs = 0; - for (k = 0; k < count; ++k) { - /* FIXME: optimize partial read */ - size_t len = vector[k].iov_len * bits_per_sample / 8; - memcpy(vector[k].iov_base, base + ofs, len); - ofs += len; - } return ctrl->result; } -static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status) +static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; @@ -515,16 +410,17 @@ static int snd_pcm_client_shm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t fd = snd_pcm_client_shm_action_fd(pcm); if (fd < 0) return fd; + /* FIXME: not mmap */ ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_STATUS); close(fd); if (ptr == MAP_FAILED || ptr == NULL) return -errno; - *status = ptr; + pcm->mmap_status = ptr; return 0; } -static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control) +static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; @@ -534,16 +430,17 @@ static int snd_pcm_client_shm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_ fd = snd_pcm_client_shm_action_fd(pcm); if (fd < 0) return fd; + /* FIXME: not mmap */ ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_CONTROL); close(fd); if (ptr == MAP_FAILED || ptr == NULL) return -errno; - *control = ptr; + pcm->mmap_control = ptr; return 0; } -static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize ATTRIBUTE_UNUSED) +static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; @@ -554,19 +451,19 @@ static int snd_pcm_client_shm_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bs fd = snd_pcm_client_shm_action_fd(pcm); if (fd < 0) return fd; + /* FIXME: not mmap */ prot = pcm->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ; - ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED, + ptr = mmap(NULL, pcm->setup.mmap_bytes, prot, MAP_FILE|MAP_SHARED, fd, SND_PCM_MMAP_OFFSET_DATA); close(fd); if (ptr == MAP_FAILED || ptr == NULL) return -errno; - *buffer = ptr; + pcm->mmap_data = ptr; return 0; } -static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED) +static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm) { -#if 0 snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; @@ -574,17 +471,14 @@ static int snd_pcm_client_shm_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; - return ctrl->result; -#else - if (munmap(status, sizeof(*status)) < 0) + /* FIXME: not mmap */ + if (munmap(pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0) return -errno; - return 0; -#endif + return ctrl->result; } -static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED) +static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm) { -#if 0 snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; @@ -592,17 +486,14 @@ static int snd_pcm_client_shm_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, sn err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; - return ctrl->result; -#else - if (munmap(control, sizeof(*control)) < 0) + /* FIXME: not mmap */ + if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0) return -errno; - return 0; -#endif + return ctrl->result; } -static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer, size_t bsize) +static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm) { -#if 0 snd_pcm_client_t *client = pcm->private; snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; int err; @@ -610,22 +501,33 @@ static int snd_pcm_client_shm_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void err = snd_pcm_client_shm_action(pcm); if (err < 0) return err; - return ctrl->result; -#else - if (munmap(buffer, bsize) < 0) + /* FIXME: not mmap */ + if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0) return -errno; - return 0; -#endif + return ctrl->result; } -static int snd_pcm_client_file_descriptor(snd_pcm_t *pcm) +static ssize_t snd_pcm_client_mmap_forward(snd_pcm_t *pcm, size_t size) +{ + snd_pcm_client_t *client = pcm->private; + snd_pcm_client_shm_t *ctrl = client->u.shm.ctrl; + int err; + ctrl->cmd = SND_PCM_IOCTL_MMAP_FORWARD; + ctrl->u.mmap_forward = size; + err = snd_pcm_client_shm_action(pcm); + if (err < 0) + return err; + return ctrl->result; +} + +static int snd_pcm_client_poll_descriptor(snd_pcm_t *pcm) { snd_pcm_client_t *client = pcm->private; return client->data_fd; } static int snd_pcm_client_channels_mask(snd_pcm_t *pcm ATTRIBUTE_UNUSED, - bitset_t *client_vmask ATTRIBUTE_UNUSED) + bitset_t *cmask ATTRIBUTE_UNUSED) { return 0; } @@ -645,35 +547,37 @@ struct snd_pcm_ops snd_pcm_client_ops = { params_info: snd_pcm_client_shm_params_info, params: snd_pcm_client_shm_params, setup: snd_pcm_client_shm_setup, + channel_info: snd_pcm_client_shm_channel_info, + channel_params: snd_pcm_client_shm_channel_params, + channel_setup: snd_pcm_client_shm_channel_setup, dump: snd_pcm_client_dump, + nonblock: snd_pcm_client_shm_nonblock, + mmap_status: snd_pcm_client_shm_mmap_status, + mmap_control: snd_pcm_client_shm_mmap_control, + mmap_data: snd_pcm_client_shm_mmap_data, + munmap_status: snd_pcm_client_shm_munmap_status, + munmap_control: snd_pcm_client_shm_munmap_control, + munmap_data: snd_pcm_client_shm_munmap_data, }; struct snd_pcm_fast_ops snd_pcm_client_fast_ops = { - nonblock: snd_pcm_client_shm_nonblock, - channel_info: snd_pcm_client_shm_channel_info, - channel_params: snd_pcm_client_shm_channel_params, - channel_setup: snd_pcm_client_shm_channel_setup, status: snd_pcm_client_shm_status, - hw_ptr: snd_pcm_client_shm_hw_ptr, state: snd_pcm_client_shm_state, + delay: snd_pcm_client_shm_delay, prepare: snd_pcm_client_shm_prepare, - go: snd_pcm_client_shm_go, - drain: snd_pcm_client_shm_drain, + start: snd_pcm_client_shm_start, + stop: snd_pcm_client_shm_stop, flush: snd_pcm_client_shm_flush, pause: snd_pcm_client_shm_pause, appl_ptr: snd_pcm_client_shm_appl_ptr, - write: snd_pcm_client_shm_write, - writev: snd_pcm_client_shm_writev, - read: snd_pcm_client_shm_read, - readv: snd_pcm_client_shm_readv, - mmap_status: snd_pcm_client_shm_mmap_status, - mmap_control: snd_pcm_client_shm_mmap_control, - mmap_data: snd_pcm_client_shm_mmap_data, - munmap_status: snd_pcm_client_shm_munmap_status, - munmap_control: snd_pcm_client_shm_munmap_control, - munmap_data: snd_pcm_client_shm_munmap_data, - file_descriptor: snd_pcm_client_file_descriptor, + writei: snd_pcm_mmap_writei, + writen: snd_pcm_mmap_writen, + readi: snd_pcm_mmap_readi, + readn: snd_pcm_mmap_readn, + poll_descriptor: snd_pcm_client_poll_descriptor, channels_mask: snd_pcm_client_channels_mask, + avail_update: snd_pcm_client_avail_update, + mmap_forward: snd_pcm_client_mmap_forward, }; static int make_local_socket(const char *filename) @@ -830,14 +734,8 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo } } - handle = calloc(1, sizeof(snd_pcm_t)); - if (!handle) { - result = -ENOMEM; - goto _err; - } client = calloc(1, sizeof(snd_pcm_client_t)); - if (!handle) { - free(handle); + if (!client) { result = -ENOMEM; goto _err; } @@ -849,6 +747,13 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo client->u.shm.ctrl = ctrl; break; } + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(client); + result = -ENOMEM; + goto _err; + } handle->type = SND_PCM_TYPE_CLIENT; handle->stream = stream; handle->ops = &snd_pcm_client_ops; @@ -857,6 +762,11 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo handle->fast_op_arg = handle; handle->mode = mode; handle->private = client; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } *handlep = handle; return 0; @@ -874,3 +784,59 @@ int snd_pcm_client_create(snd_pcm_t **handlep, char *host, int port, int transpo return result; } +int _snd_pcm_client_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *socket = NULL; + char *sname = NULL; + char *host = NULL; + long port = -1; + int err; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "socket") == 0) { + err = snd_config_string_get(n, &socket); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "host") == 0) { + err = snd_config_string_get(n, &host); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "port") == 0) { + err = snd_config_integer_get(n, &port); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname) + return -EINVAL; + if (socket) { + if (port >= 0 || host) + return -EINVAL; + return snd_pcm_client_create(pcmp, socket, -1, SND_TRANSPORT_TYPE_SHM, sname, stream, mode); + } else { + if (port < 0 || !name) + return -EINVAL; + return snd_pcm_client_create(pcmp, host, port, SND_TRANSPORT_TYPE_TCP, sname, stream, mode); + } +} + diff --git a/src/pcm/pcm_common.c b/src/pcm/pcm_common.c deleted file mode 100644 index ae947ecf..00000000 --- a/src/pcm/pcm_common.c +++ /dev/null @@ -1,1274 +0,0 @@ -/* - * PCM Plug-In shared (kernel/library) code - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> - * - * - * 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. - * - */ - -#if 0 -#define PLUGIN_DEBUG -#endif -#ifdef __KERNEL__ -#include "../../include/driver.h" -#include "../../include/pcm.h" -#define snd_pcm_plug_first(plug) ((plug)->runtime->oss.plugin_first) -#define snd_pcm_plug_last(plug) ((plug)->runtime->oss.plugin_last) -#define __vmalloc snd_vmalloc -#define __vfree snd_vfree -#else -#include <malloc.h> -#include <errno.h> -#include <stdio.h> -#include <string.h> -#include <sys/uio.h> -#include "pcm_local.h" -#define snd_pcm_plug_first(plug) ((plug)->first) -#define snd_pcm_plug_last(plug) ((plug)->last) -#define __vmalloc malloc -#define __vfree free -#endif - -static int snd_pcm_plugin_src_channels_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.channels); - *src_vmask = vmask; - return 0; -} - -static int snd_pcm_plugin_dst_channels_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.channels); - *dst_vmask = vmask; - return 0; -} - -static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, size_t frames) -{ - snd_pcm_format_t *format; - ssize_t width; - size_t size; - unsigned int channel; - snd_pcm_plugin_channel_t *c; - if (plugin->stream == SND_PCM_STREAM_PLAYBACK) - format = &plugin->src_format; - else - format = &plugin->dst_format; - if ((width = snd_pcm_format_physical_width(format->format)) < 0) - return width; - size = frames * format->channels * width; - assert(size % 8 == 0); - size /= 8; - if (plugin->buf_frames < frames) { - if (plugin->buf) - __vfree(plugin->buf); - plugin->buf = __vmalloc(size); - plugin->buf_frames = frames; - } - if (!plugin->buf) - return -ENOMEM; - c = plugin->buf_channels; - if (format->interleave) { - for (channel = 0; channel < format->channels; channel++, c++) { - c->enabled = 1; - c->wanted = 0; - c->area.addr = plugin->buf; - c->area.first = channel * width; - c->area.step = format->channels * width; - } - } else { - assert(size % format->channels == 0); - size /= format->channels; - for (channel = 0; channel < format->channels; channel++, c++) { - c->enabled = 1; - c->wanted = 0; - c->area.addr = plugin->buf + (channel * size); - c->area.first = 0; - c->area.step = width; - } - } - return 0; -} - -int snd_pcm_plug_alloc(snd_pcm_plug_t *plug, size_t frames) -{ - int err; -#ifndef __KERNEL__ - plug->frames_alloc = frames; -#endif - assert(snd_pcm_plug_first(plug)); - if (snd_pcm_plug_stream(plug) == SND_PCM_STREAM_PLAYBACK) { - snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); - while (plugin->next) { - if (plugin->dst_frames) - frames = plugin->dst_frames(plugin, frames); - assert(frames > 0); - plugin = plugin->next; - err = snd_pcm_plugin_alloc(plugin, frames); - if (err < 0) - return err; - } - } else { - snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); - while (plugin->prev) { - if (plugin->src_frames) - frames = plugin->src_frames(plugin, frames); - assert(frames > 0); - plugin = plugin->prev; - err = snd_pcm_plugin_alloc(plugin, frames); - if (err < 0) - return err; - } - } - return 0; -} - - -ssize_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels) -{ - assert(frames <= plugin->buf_frames); - *channels = plugin->buf_channels; - return frames; -} - -int snd_pcm_plugin_build(snd_pcm_plug_t *plug, - const char *name, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - size_t extra, - snd_pcm_plugin_t **ret) -{ - snd_pcm_plugin_t *plugin; - size_t channels; - - assert(plug); - assert(src_format && dst_format); - plugin = (snd_pcm_plugin_t *)calloc(1, sizeof(*plugin) + extra); - if (plugin == NULL) - return -ENOMEM; - plugin->name = name ? strdup(name) : NULL; - plugin->plug = plug; - plugin->stream = snd_pcm_plug_stream(plug); - plugin->src_format = *src_format; - plugin->src_width = snd_pcm_format_physical_width(src_format->format); - assert(plugin->src_width > 0); - plugin->dst_format = *dst_format; - plugin->dst_width = snd_pcm_format_physical_width(dst_format->format); - assert(plugin->dst_width > 0); - if (plugin->stream == SND_PCM_STREAM_PLAYBACK) - channels = src_format->channels; - else - channels = dst_format->channels; - plugin->buf_channels = calloc(channels, sizeof(*plugin->buf_channels)); - if (plugin->buf_channels == NULL) { - free(plugin); - return -ENOMEM; - } - plugin->src_vmask = bitset_alloc(src_format->channels); - if (plugin->src_vmask == NULL) { - free(plugin->buf_channels); - free(plugin); - return -ENOMEM; - } - plugin->dst_vmask = bitset_alloc(dst_format->channels); - if (plugin->dst_vmask == NULL) { - free(plugin->buf_channels); - free(plugin->src_vmask); - free(plugin); - return -ENOMEM; - } - plugin->client_channels = snd_pcm_plugin_client_channels; - plugin->src_channels_mask = snd_pcm_plugin_src_channels_mask; - plugin->dst_channels_mask = snd_pcm_plugin_dst_channels_mask; - *ret = plugin; - return 0; -} - -int snd_pcm_plugin_free(snd_pcm_plugin_t *plugin) -{ - assert(plugin); - if (plugin->private_free) - plugin->private_free(plugin); - if (plugin->name) - free(plugin->name); - free(plugin->buf_channels); - if (plugin->buf) - __vfree(plugin->buf); - free(plugin->src_vmask); - free(plugin->dst_vmask); - free(plugin); - return 0; -} - -ssize_t snd_pcm_plug_client_size(snd_pcm_plug_t *plug, size_t drv_frames) -{ - snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; - int stream = snd_pcm_plug_stream(plug); - - assert(plug); - if (drv_frames == 0) - return 0; - if (stream == SND_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_last(plug); - while (plugin && drv_frames > 0) { - plugin_prev = plugin->prev; - if (plugin->src_frames) - drv_frames = plugin->src_frames(plugin, drv_frames); - plugin = plugin_prev; - } - } else if (stream == SND_PCM_STREAM_CAPTURE) { - plugin = snd_pcm_plug_first(plug); - while (plugin && drv_frames > 0) { - plugin_next = plugin->next; - if (plugin->dst_frames) - drv_frames = plugin->dst_frames(plugin, drv_frames); - plugin = plugin_next; - } - } else - assert(0); - return drv_frames; -} - -ssize_t snd_pcm_plug_slave_size(snd_pcm_plug_t *plug, size_t clt_frames) -{ - snd_pcm_plugin_t *plugin, *plugin_prev, *plugin_next; - ssize_t frames; - int stream = snd_pcm_plug_stream(plug); - - assert(plug); - if (clt_frames == 0) - return 0; - frames = clt_frames; - if (stream == SND_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_first(plug); - while (plugin && frames > 0) { - plugin_next = plugin->next; - if (plugin->dst_frames) { - frames = plugin->dst_frames(plugin, frames); - if (frames < 0) - return frames; - } - plugin = plugin_next; - } - } else if (stream == SND_PCM_STREAM_CAPTURE) { - plugin = snd_pcm_plug_last(plug); - while (plugin) { - plugin_prev = plugin->prev; - if (plugin->src_frames) { - frames = plugin->src_frames(plugin, frames); - if (frames < 0) - return frames; - } - plugin = plugin_prev; - } - } else - assert(0); - return frames; -} - -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 | - SND_PCM_FMT_U16_BE | SND_PCM_FMT_S16_BE | - SND_PCM_FMT_U24_LE | SND_PCM_FMT_S24_LE | - SND_PCM_FMT_U24_BE | SND_PCM_FMT_S24_BE | - SND_PCM_FMT_U32_LE | SND_PCM_FMT_S32_LE | - SND_PCM_FMT_U32_BE | SND_PCM_FMT_S32_BE); - formats |= SND_PCM_FMT_MU_LAW; -#ifndef __KERNEL__ - formats |= SND_PCM_FMT_A_LAW | SND_PCM_FMT_IMA_ADPCM; -#endif - - if (formats & linfmts) - formats |= linfmts; - return formats; -} - -static int preferred_formats[] = { - SND_PCM_SFMT_S16_LE, - SND_PCM_SFMT_S16_BE, - SND_PCM_SFMT_U16_LE, - SND_PCM_SFMT_U16_BE, - SND_PCM_SFMT_S24_LE, - SND_PCM_SFMT_S24_BE, - SND_PCM_SFMT_U24_LE, - SND_PCM_SFMT_U24_BE, - SND_PCM_SFMT_S32_LE, - SND_PCM_SFMT_S32_BE, - SND_PCM_SFMT_U32_LE, - SND_PCM_SFMT_U32_BE, - SND_PCM_SFMT_S8, - SND_PCM_SFMT_U8 -}; - -int snd_pcm_plug_slave_fmt(int format, snd_pcm_params_info_t *slave_info) -{ - 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); - int unsignd = snd_pcm_format_unsigned(format); - int big = snd_pcm_format_big_endian(format); - int format1; - int wid, width1=width; - int dwidth1 = 8; - for (wid = 0; wid < 4; ++wid) { - int end, big1 = big; - for (end = 0; end < 2; ++end) { - int sgn, unsignd1 = unsignd; - for (sgn = 0; sgn < 2; ++sgn) { - format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); - if (format1 >= 0 && - slave_info->formats & (1 << format1)) - goto _found; - unsignd1 = !unsignd1; - } - big1 = !big1; - } - if (width1 == 32) { - dwidth1 = -dwidth1; - width1 = width; - } - width1 += dwidth1; - } - return -EINVAL; - _found: - return format1; - } else { - unsigned int i; - switch (format) { - case SND_PCM_SFMT_MU_LAW: -#ifndef __KERNEL__ - case SND_PCM_SFMT_A_LAW: - case SND_PCM_SFMT_IMA_ADPCM: -#endif - for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { - int format1 = preferred_formats[i]; - if (slave_info->formats & (1 << format1)) - return format1; - } - default: - return -EINVAL; - } - } -} - -struct { - unsigned int rate; - unsigned int flag; -} snd_pcm_rates[] = { - { 8000, SND_PCM_RATE_8000 }, - { 11025, SND_PCM_RATE_11025 }, - { 16000, SND_PCM_RATE_16000 }, - { 22050, SND_PCM_RATE_22050 }, - { 32000, SND_PCM_RATE_32000 }, - { 44100, SND_PCM_RATE_44100 }, - { 48000, SND_PCM_RATE_48000 }, - { 88200, SND_PCM_RATE_88200 }, - { 96000, SND_PCM_RATE_96000 }, - { 176400, SND_PCM_RATE_176400 }, - { 192000, SND_PCM_RATE_192000 } -}; - -int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info) -{ - if (rate <= slave_info->min_rate) - return slave_info->min_rate; - else if (rate >= slave_info->max_rate) - return slave_info->max_rate; - else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS | - SND_PCM_RATE_KNOT))) { - unsigned int k; - unsigned int rate1 = 0, rate2 = 0; - int delta1, delta2; - for (k = 0; k < sizeof(snd_pcm_rates) / - sizeof(snd_pcm_rates[0]); ++k) { - if (!(snd_pcm_rates[k].flag & slave_info->rates)) - continue; - if (snd_pcm_rates[k].rate < rate) { - rate1 = snd_pcm_rates[k].rate; - } else if (snd_pcm_rates[k].rate >= rate) { - rate2 = snd_pcm_rates[k].rate; - break; - } - } - if (rate1 == 0) - return rate2; - if (rate2 == 0) - return rate1; - delta1 = rate - rate1; - delta2 = rate2 - rate; - if (delta1 < delta2) - return rate1; - else - return rate2; - } - return rate; -} - -int snd_pcm_plug_slave_format(snd_pcm_format_t *format, - snd_pcm_info_t *slave_info, - snd_pcm_params_info_t *slave_params_info, - snd_pcm_format_t *slave_format) -{ - int slave_rate; - *slave_format = *format; - if ((slave_params_info->formats & (1 << format->format)) == 0) { - int slave_fmt = snd_pcm_plug_slave_fmt(format->format, slave_params_info); - if (slave_fmt < 0) - return slave_fmt; - slave_format->format = slave_fmt; - } - - /* channels */ - if (format->channels < slave_params_info->min_channels) - slave_format->channels = slave_params_info->min_channels; - else if (format->channels > slave_params_info->max_channels) - slave_format->channels = slave_params_info->max_channels; - - /* rate */ - slave_rate = snd_pcm_plug_slave_rate(format->rate, slave_params_info); - if (slave_rate < 0) - return slave_rate; - slave_format->rate = slave_rate; - - /* interleave */ - if (!(slave_info->flags & SND_PCM_INFO_INTERLEAVE)) - slave_format->interleave = 0; - if (!(slave_info->flags & SND_PCM_INFO_NONINTERLEAVE)) - slave_format->interleave = 1; - return 0; -} - -int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_format_t *slave_format) -{ - snd_pcm_format_t tmpformat; - snd_pcm_format_t dstformat; - snd_pcm_format_t *srcformat; - snd_pcm_plugin_t *plugin; - int err; - - switch (snd_pcm_plug_stream(plug)) { - case SND_PCM_STREAM_PLAYBACK: - dstformat = *slave_format; - srcformat = slave_format; - *srcformat = *format; - break; - case SND_PCM_STREAM_CAPTURE: - dstformat = *format; - srcformat = format; - *srcformat = *slave_format; - break; - default: - assert(0); - return -EINVAL; - } - tmpformat = *srcformat; - - pdprintf("srcformat: interleave=%i, format=%i, rate=%i, channels=%i\n", - srcformat->interleave, - srcformat->format, - srcformat->rate, - srcformat->channels); - pdprintf("dstformat: interleave=%i, format=%i, rate=%i, channels=%i\n", - dstformat.interleave, - dstformat.format, - dstformat.rate, - dstformat.channels); - - if (srcformat->channels == 1) - srcformat->interleave = dstformat.interleave; - - /* Format change (linearization) */ - if ((srcformat->format != dstformat.format || - srcformat->rate != dstformat.rate || - srcformat->channels != dstformat.channels) && - !snd_pcm_format_linear(srcformat->format)) { - if (snd_pcm_format_linear(dstformat.format)) - tmpformat.format = dstformat.format; - else - tmpformat.format = SND_PCM_SFMT_S16; - tmpformat.interleave = dstformat.interleave; - switch (srcformat->format) { - case SND_PCM_SFMT_MU_LAW: - err = snd_pcm_plugin_build_mulaw(plug, - srcformat, &tmpformat, - &plugin); - break; -#ifndef __KERNEL__ - case SND_PCM_SFMT_A_LAW: - err = snd_pcm_plugin_build_alaw(plug, - srcformat, &tmpformat, - &plugin); - break; - case SND_PCM_SFMT_IMA_ADPCM: - err = snd_pcm_plugin_build_adpcm(plug, - srcformat, &tmpformat, - &plugin); - break; -#endif - default: - return -EINVAL; - } - pdprintf("format format change: src=%i, dst=%i returns %i\n", srcformat->format, tmpformat.format, err); - if (err < 0) - return err; - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - /* channels reduction */ - if (srcformat->channels > dstformat.channels) { - int sv = srcformat->channels; - int dv = dstformat.channels; - route_ttable_entry_t *ttable = calloc(1, dv*sv*sizeof(*ttable)); -#if 1 - if (sv == 2 && dv == 1) { - ttable[0] = HALF; - ttable[1] = HALF; - } else -#endif - { - int v; - for (v = 0; v < dv; ++v) - ttable[v * sv + v] = FULL; - } - tmpformat.channels = dstformat.channels; - tmpformat.interleave = dstformat.interleave; - if (srcformat->rate == dstformat.rate && - snd_pcm_format_linear(dstformat.format)) - tmpformat.format = dstformat.format; - err = snd_pcm_plugin_build_route(plug, - srcformat, &tmpformat, - ttable, &plugin); - free(ttable); - pdprintf("format channels reduction: src=%i, dst=%i returns %i\n", srcformat->channels, tmpformat.channels, err); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - /* rate resampling */ - if (srcformat->rate != dstformat.rate) { - tmpformat.rate = dstformat.rate; - tmpformat.interleave = dstformat.interleave; - if (srcformat->channels == dstformat.channels && - snd_pcm_format_linear(dstformat.format)) - tmpformat.format = dstformat.format; - err = snd_pcm_plugin_build_rate(plug, - srcformat, &tmpformat, - &plugin); - pdprintf("format rate down resampling: src=%i, dst=%i returns %i\n", srcformat->rate, tmpformat.rate, err); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - /* channels extension */ - if (srcformat->channels < dstformat.channels) { - int sv = srcformat->channels; - int dv = dstformat.channels; - route_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 channels */ - int vd, vs; - for (vd = 0, vs = 0; vd < dv; ++vd) { - ttable[vd * sv + vs] = FULL; - vs++; - if (vs == sv) - vs = 0; - } - } -#endif - tmpformat.channels = dstformat.channels; - tmpformat.interleave = dstformat.interleave; - if (snd_pcm_format_linear(dstformat.format)) - tmpformat.format = dstformat.format; - err = snd_pcm_plugin_build_route(plug, - srcformat, &tmpformat, - ttable, &plugin); - free(ttable); - pdprintf("format channels extension: src=%i, dst=%i returns %i\n", srcformat->channels, tmpformat.channels, err); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - /* format change */ - if (srcformat->format != dstformat.format) { - tmpformat.format = dstformat.format; - tmpformat.interleave = dstformat.interleave; - if (tmpformat.format == SND_PCM_SFMT_MU_LAW) { - err = snd_pcm_plugin_build_mulaw(plug, - srcformat, &tmpformat, - &plugin); - } -#ifndef __KERNEL__ - else if (tmpformat.format == SND_PCM_SFMT_A_LAW) { - err = snd_pcm_plugin_build_alaw(plug, - srcformat, &tmpformat, - &plugin); - } - else if (tmpformat.format == SND_PCM_SFMT_IMA_ADPCM) { - err = snd_pcm_plugin_build_adpcm(plug, - srcformat, &tmpformat, - &plugin); - } -#endif - else if (snd_pcm_format_linear(srcformat->format) && - snd_pcm_format_linear(tmpformat.format)) { - err = snd_pcm_plugin_build_linear(plug, - srcformat, &tmpformat, - &plugin); - } - else - return -EINVAL; - pdprintf("format format change: src=%i, dst=%i returns %i\n", srcformat->format, tmpformat.format, err); - if (err < 0) - return err; - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - /* interleave */ - if (srcformat->interleave != dstformat.interleave) { - tmpformat.interleave = dstformat.interleave; - err = snd_pcm_plugin_build_copy(plug, - srcformat, &tmpformat, - &plugin); - pdprintf("interleave change: src=%i, dst=%i returns %i\n", srcformat->interleave, tmpformat.interleave, err); - if (err < 0) - return err; - err = snd_pcm_plugin_append(plugin); - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - *srcformat = tmpformat; - } - - return 0; -} - -ssize_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, - char *buf, - size_t count, - snd_pcm_plugin_channel_t **channels) -{ - snd_pcm_plugin_t *plugin; - snd_pcm_plugin_channel_t *v; - snd_pcm_format_t *format; - int width, nchannels, channel; - int stream = snd_pcm_plug_stream(plug); - - assert(buf); - if (stream == SND_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_first(plug); - format = &plugin->src_format; - } - else { - plugin = snd_pcm_plug_last(plug); - format = &plugin->dst_format; - } - v = plugin->buf_channels; - *channels = v; - if ((width = snd_pcm_format_physical_width(format->format)) < 0) - return width; - nchannels = format->channels; - assert(format->interleave || format->channels == 1); - for (channel = 0; channel < nchannels; channel++, v++) { - v->enabled = 1; - v->wanted = (stream == SND_PCM_STREAM_CAPTURE); - v->area.addr = buf; - v->area.first = channel * width; - v->area.step = nchannels * width; - } - return count; -} - -ssize_t snd_pcm_plug_client_channels_iovec(snd_pcm_plug_t *plug, - const struct iovec *vector, - unsigned long count, - snd_pcm_plugin_channel_t **channels) -{ - snd_pcm_plugin_t *plugin; - snd_pcm_plugin_channel_t *v; - snd_pcm_format_t *format; - int width; - unsigned int nchannels, channel; - int stream = snd_pcm_plug_stream(plug); - - if (stream == SND_PCM_STREAM_PLAYBACK) { - plugin = snd_pcm_plug_first(plug); - format = &plugin->src_format; - } - else { - plugin = snd_pcm_plug_last(plug); - format = &plugin->dst_format; - } - v = plugin->buf_channels; - *channels = v; - if ((width = snd_pcm_format_physical_width(format->format)) < 0) - return width; - nchannels = format->channels; - if (format->interleave) { - assert(count == 1 && vector->iov_base); - - for (channel = 0; channel < nchannels; channel++, v++) { - v->enabled = 1; - v->wanted = (stream == SND_PCM_STREAM_CAPTURE); - v->area.addr = vector->iov_base; - v->area.first = channel * width; - v->area.step = nchannels * width; - } - return vector->iov_len; - } else { - size_t len; - assert(count == nchannels); - len = vector->iov_len; - for (channel = 0; channel < nchannels; channel++, v++, vector++) { - assert(vector->iov_len == len); - v->enabled = (vector->iov_base != NULL); - v->wanted = (v->enabled && (stream == SND_PCM_STREAM_CAPTURE)); - v->area.addr = vector->iov_base; - v->area.first = 0; - v->area.step = width; - } - return len; - } -} - -int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, - bitset_t *client_vmask) -{ - snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); - if (plugin == NULL) { -#ifndef __KERNEL__ - return snd_pcm_channels_mask(plug->slave, client_vmask); -#else - return 0; -#endif - } else { - int schannels = plugin->dst_format.channels; - bitset_t bs[bitset_size(schannels)]; - bitset_t *srcmask; - bitset_t *dstmask = bs; - int err; - bitset_one(dstmask, schannels); -#ifndef __KERNEL__ - err = snd_pcm_channels_mask(plug->slave, dstmask); - if (err < 0) - return err; -#endif - if (plugin == NULL) { - bitset_and(client_vmask, dstmask, schannels); - return 0; - } - while (1) { - err = plugin->src_channels_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.channels); - return 0; - } -} - -int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug, - bitset_t *client_vmask) -{ - snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); - if (plugin == NULL) { -#ifndef __KERNEL__ - return snd_pcm_channels_mask(plug->slave, client_vmask); -#else - return 0; -#endif - } else { - int schannels = plugin->src_format.channels; - bitset_t bs[bitset_size(schannels)]; - bitset_t *srcmask = bs; - bitset_t *dstmask; - int err; - bitset_one(srcmask, schannels); -#ifndef __KERNEL__ - err = snd_pcm_channels_mask(plug->slave, srcmask); - if (err < 0) - return err; -#endif - while (1) { - err = plugin->dst_channels_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.channels); - return 0; - } -} - -static int snd_pcm_plug_playback_disable_useless_channels(snd_pcm_plug_t *plug, - snd_pcm_plugin_channel_t *src_channels) -{ - snd_pcm_plugin_t *plugin = snd_pcm_plug_first(plug); - unsigned int nchannels = plugin->src_format.channels; - bitset_t bs[bitset_size(nchannels)]; - bitset_t *srcmask = bs; - int err; - unsigned int channel; - for (channel = 0; channel < nchannels; channel++) { - if (src_channels[channel].enabled) - bitset_set(srcmask, channel); - else - bitset_reset(srcmask, channel); - } - err = snd_pcm_plug_playback_channels_mask(plug, srcmask); - if (err < 0) - return err; - for (channel = 0; channel < nchannels; channel++) { - if (!bitset_get(srcmask, channel)) - src_channels[channel].enabled = 0; - } - return 0; -} - -static int snd_pcm_plug_capture_disable_useless_channels(snd_pcm_plug_t *plug, - snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *client_channels) -{ - snd_pcm_plugin_t *plugin = snd_pcm_plug_last(plug); - unsigned int nchannels = plugin->dst_format.channels; - bitset_t bs[bitset_size(nchannels)]; - bitset_t *dstmask = bs; - bitset_t *srcmask; - int err; - unsigned int channel; - for (channel = 0; channel < nchannels; channel++) { - if (client_channels[channel].enabled) - bitset_set(dstmask, channel); - else - bitset_reset(dstmask, channel); - } - while (plugin) { - err = plugin->src_channels_mask(plugin, dstmask, &srcmask); - if (err < 0) - return err; - dstmask = srcmask; - plugin = plugin->prev; - } -#ifndef __KERNEL__ - err = snd_pcm_channels_mask(plug->slave, dstmask); - if (err < 0) - return err; -#endif - plugin = snd_pcm_plug_first(plug); - nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; channel++) { - if (!bitset_get(dstmask, channel)) - src_channels[channel].enabled = 0; - } - return 0; -} - -ssize_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, size_t size) -{ - snd_pcm_plugin_t *plugin, *next; - snd_pcm_plugin_channel_t *dst_channels; - int err; - ssize_t frames = size; - - if ((err = snd_pcm_plug_playback_disable_useless_channels(plug, src_channels)) < 0) - return err; - - plugin = snd_pcm_plug_first(plug); - while (plugin && frames > 0) { - if ((next = plugin->next) != NULL) { - ssize_t frames1 = frames; - if (plugin->dst_frames) - frames1 = plugin->dst_frames(plugin, frames); - if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) { - return err; - } - if (err != frames1) { - frames = err; - if (plugin->src_frames) - frames = plugin->src_frames(plugin, frames1); - } - } else - dst_channels = 0; - pdprintf("write plugin: %s, %i\n", plugin->name, frames); - if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) - return frames; - src_channels = dst_channels; - plugin = next; - } - return snd_pcm_plug_client_size(plug, frames); -} - -ssize_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, size_t size) -{ - snd_pcm_plugin_t *plugin, *next; - snd_pcm_plugin_channel_t *src_channels, *dst_channels; - ssize_t frames = size; - int err; - - frames = snd_pcm_plug_slave_size(plug, frames); - if (frames < 0) - return frames; - - src_channels = 0; - plugin = snd_pcm_plug_first(plug); - while (plugin && frames > 0) { - if ((next = plugin->next) != NULL) { - if ((err = plugin->client_channels(plugin, frames, &dst_channels)) < 0) { - return err; - } - frames = err; - if (!plugin->prev) { - if ((err = snd_pcm_plug_capture_disable_useless_channels(plug, dst_channels, dst_channels_final) < 0)) - return err; - } - } else { - dst_channels = dst_channels_final; - } - pdprintf("read plugin: %s, %i\n", plugin->name, frames); - if ((frames = plugin->transfer(plugin, src_channels, dst_channels, frames)) < 0) - return frames; - plugin = next; - src_channels = dst_channels; - } - return frames; -} - -int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset, - size_t samples, int format) -{ - /* FIXME: sub byte resolution and odd dst_offset */ - char *dst; - unsigned int dst_step; - int width; - u_int64_t silence; - if (!dst_area->addr) - return 0; - dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; - width = snd_pcm_format_physical_width(format); - silence = snd_pcm_format_silence_64(format); - if (dst_area->step == (unsigned int) width) { - size_t dwords = samples * width / 64; - samples -= dwords * 64 / width; - while (dwords-- > 0) - *((u_int64_t*)dst)++ = silence; - if (samples == 0) - return 0; - } - dst_step = dst_area->step / 8; - switch (width) { - case 4: { - u_int8_t s0 = silence & 0xf0; - u_int8_t s1 = silence & 0x0f; - int dstbit = dst_area->first % 8; - int dstbit_step = dst_area->step % 8; - while (samples-- > 0) { - if (dstbit) { - *dst &= 0xf0; - *dst |= s1; - } else { - *dst &= 0x0f; - *dst |= s0; - } - dst += dst_step; - dstbit += dstbit_step; - if (dstbit == 8) { - dst++; - dstbit = 0; - } - } - break; - } - case 8: { - u_int8_t sil = silence; - while (samples-- > 0) { - *dst = sil; - dst += dst_step; - } - break; - } - case 16: { - u_int16_t sil = silence; - while (samples-- > 0) { - *(u_int16_t*)dst = sil; - dst += dst_step; - } - break; - } - case 32: { - u_int32_t sil = silence; - while (samples-- > 0) { - *(u_int32_t*)dst = sil; - dst += dst_step; - } - break; - } - case 64: { - while (samples-- > 0) { - *(u_int64_t*)dst = silence; - dst += dst_step; - } - break; - } - default: - assert(0); - } - return 0; -} - -int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, size_t dst_offset, - size_t vcount, size_t frames, int format) -{ - int width = snd_pcm_format_physical_width(format); - while (vcount > 0) { - void *addr = dst_areas->addr; - unsigned int step = dst_areas->step; - const snd_pcm_channel_area_t *begin = dst_areas; - int vc = vcount; - unsigned int v = 0; - int err; - while (1) { - vc--; - v++; - dst_areas++; - if (vc == 0 || - dst_areas->addr != addr || - dst_areas->step != step || - dst_areas->first != dst_areas[-1].first + width) - break; - } - if (v > 1 && v * width == step) { - /* Collapse the areas */ - snd_pcm_channel_area_t d; - d.addr = begin->addr; - d.first = begin->first; - d.step = width; - err = snd_pcm_area_silence(&d, dst_offset * v, frames * v, format); - vcount -= v; - } else { - err = snd_pcm_area_silence(begin, dst_offset, frames, format); - dst_areas = begin + 1; - vcount--; - } - if (err < 0) - return err; - } - return 0; -} - - -int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset, - const snd_pcm_channel_area_t *dst_area, size_t dst_offset, - size_t samples, int format) -{ - /* FIXME: sub byte resolution and odd dst_offset */ - char *src, *dst; - int width; - int src_step, dst_step; - src = src_area->addr + (src_area->first + src_area->step * src_offset) / 8; - if (!src_area->addr) - return snd_pcm_area_silence(dst_area, dst_offset, samples, format); - dst = dst_area->addr + (dst_area->first + dst_area->step * dst_offset) / 8; - if (!dst_area->addr) - return 0; - width = snd_pcm_format_physical_width(format); - if (src_area->step == (unsigned int) width && - dst_area->step == (unsigned int) width) { - size_t bytes = samples * width / 8; - samples -= bytes * 8 / width; - memcpy(dst, src, bytes); - if (samples == 0) - return 0; - } - src_step = src_area->step / 8; - dst_step = dst_area->step / 8; - switch (width) { - case 4: { - int srcbit = src_area->first % 8; - int srcbit_step = src_area->step % 8; - int dstbit = dst_area->first % 8; - int dstbit_step = dst_area->step % 8; - while (samples-- > 0) { - unsigned char srcval; - if (srcbit) - srcval = *src & 0x0f; - else - srcval = *src & 0xf0; - if (dstbit) - *dst &= 0xf0; - else - *dst &= 0x0f; - *dst |= srcval; - src += src_step; - srcbit += srcbit_step; - if (srcbit == 8) { - src++; - srcbit = 0; - } - dst += dst_step; - dstbit += dstbit_step; - if (dstbit == 8) { - dst++; - dstbit = 0; - } - } - break; - } - case 8: { - while (samples-- > 0) { - *dst = *src; - src += src_step; - dst += dst_step; - } - break; - } - case 16: { - while (samples-- > 0) { - *(u_int16_t*)dst = *(u_int16_t*)src; - src += src_step; - dst += dst_step; - } - break; - } - case 32: { - while (samples-- > 0) { - *(u_int32_t*)dst = *(u_int32_t*)src; - src += src_step; - dst += dst_step; - } - break; - } - case 64: { - while (samples-- > 0) { - *(u_int64_t*)dst = *(u_int64_t*)src; - src += src_step; - dst += dst_step; - } - break; - } - default: - assert(0); - } - return 0; -} - -int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, size_t src_offset, - const snd_pcm_channel_area_t *dst_areas, size_t dst_offset, - size_t vcount, size_t frames, int format) -{ - int width = snd_pcm_format_physical_width(format); - while (vcount > 0) { - unsigned int step = src_areas->step; - void *src_addr = src_areas->addr; - const snd_pcm_channel_area_t *src_start = src_areas; - void *dst_addr = dst_areas->addr; - const snd_pcm_channel_area_t *dst_start = dst_areas; - int vc = vcount; - unsigned int v = 0; - while (dst_areas->step == step) { - vc--; - v++; - src_areas++; - dst_areas++; - if (vc == 0 || - src_areas->step != step || - src_areas->addr != src_addr || - dst_areas->addr != dst_addr || - src_areas->first != src_areas[-1].first + width || - dst_areas->first != dst_areas[-1].first + width) - break; - } - if (v > 1 && v * width == step) { - /* Collapse the areas */ - snd_pcm_channel_area_t s, d; - s.addr = src_start->addr; - s.first = src_start->first; - s.step = width; - d.addr = dst_start->addr; - d.first = dst_start->first; - d.step = width; - snd_pcm_area_copy(&s, src_offset * v, &d, dst_offset * v, frames * v, format); - vcount -= v; - } else { - snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format); - src_areas = src_start + 1; - dst_areas = dst_start + 1; - vcount--; - } - } - return 0; -} diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c new file mode 100644 index 00000000..644aae34 --- /dev/null +++ b/src/pcm/pcm_file.c @@ -0,0 +1,447 @@ +/* + * PCM - File plugin + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef struct { + snd_pcm_t *slave; + int close_slave; + char *fname; + int fd; +} snd_pcm_file_t; + +static int snd_pcm_file_close(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + int err = 0; + if (file->close_slave) + err = snd_pcm_close(file->slave); + if (file->fname) { + free(file->fname); + close(file->fd); + } + free(file); + return 0; +} + +static int snd_pcm_file_nonblock(snd_pcm_t *pcm, int nonblock) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_nonblock(file->slave, nonblock); +} + +static int snd_pcm_file_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_info(file->slave, info); +} + +static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_channel_info(file->slave, info); +} + +static int snd_pcm_file_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_channel_params(file->slave, params); +} + +static int snd_pcm_file_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_channel_setup(file->slave, setup); +} + +static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_status(file->slave, status); +} + +static int snd_pcm_file_state(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_state(file->slave); +} + +static int snd_pcm_file_delay(snd_pcm_t *pcm, ssize_t *delayp) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_delay(file->slave, delayp); +} + +static int snd_pcm_file_prepare(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_prepare(file->slave); +} + +static int snd_pcm_file_start(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_start(file->slave); +} + +static int snd_pcm_file_stop(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_stop(file->slave); +} + +static int snd_pcm_file_flush(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_flush(file->slave); +} + +static int snd_pcm_file_pause(snd_pcm_t *pcm, int enable) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_pause(file->slave, enable); +} + +static ssize_t snd_pcm_file_appl_ptr(snd_pcm_t *pcm, off_t offset) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_appl_ptr(file->slave, offset); +} + +static void snd_pcm_file_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, size_t frames) +{ + snd_pcm_file_t *file = pcm->private; + size_t bytes = snd_pcm_frames_to_bytes(pcm, frames); + char buf[bytes]; + size_t channels = pcm->setup.format.channels; + snd_pcm_channel_area_t buf_areas[channels]; + size_t channel; + ssize_t r; + for (channel = 0; channel < channels; ++channel) { + snd_pcm_channel_area_t *a = &buf_areas[channel]; + a->addr = buf; + a->first = pcm->bits_per_sample * channel; + a->step = pcm->bits_per_frame; + } + snd_pcm_areas_copy(areas, offset, buf_areas, 0, + channels, frames, pcm->setup.format.sfmt); + r = write(file->fd, buf, bytes); + assert(r == (ssize_t)bytes); +} + +static ssize_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, size_t size) +{ + snd_pcm_file_t *file = pcm->private; + ssize_t n = snd_pcm_writei(file->slave, buffer, size); + if (n > 0) { + size_t bytes = snd_pcm_frames_to_bytes(pcm, n); + ssize_t r = write(file->fd, buffer, bytes); + assert(r == (ssize_t)bytes); + } + return n; +} + +static ssize_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, size_t size) +{ + snd_pcm_file_t *file = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t n = snd_pcm_writen(file->slave, bufs, size); + if (n > 0) { + snd_pcm_areas_from_bufs(pcm, areas, bufs); + snd_pcm_file_write_areas(pcm, areas, 0, n); + } + return n; +} + +static ssize_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, size_t size) +{ + snd_pcm_file_t *file = pcm->private; + ssize_t n = snd_pcm_readi(file->slave, buffer, size); + if (n > 0) { + size_t bytes = snd_pcm_frames_to_bytes(pcm, n); + ssize_t r = write(file->fd, buffer, bytes); + assert(r == (ssize_t)bytes); + } + return n; +} + +static ssize_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, size_t size) +{ + snd_pcm_file_t *file = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t n = snd_pcm_writen(file->slave, bufs, size); + if (n > 0) { + snd_pcm_areas_from_bufs(pcm, areas, bufs); + snd_pcm_file_write_areas(pcm, areas, 0, n); + } + return n; +} + +static ssize_t snd_pcm_file_mmap_forward(snd_pcm_t *pcm, size_t size) +{ + snd_pcm_file_t *file = pcm->private; + size_t ofs = pcm->mmap_control->appl_ptr % pcm->setup.buffer_size; + ssize_t n = snd_pcm_mmap_forward(file->slave, size); + size_t xfer = 0; + if (n <= 0) + return n; + while (xfer < (size_t)n) { + size_t frames = size - xfer; + size_t cont = pcm->setup.buffer_size - ofs; + if (cont < frames) + frames = cont; + snd_pcm_file_write_areas(pcm, pcm->mmap_areas, ofs, frames); + ofs += frames; + if (ofs == pcm->setup.buffer_size) + ofs = 0; + xfer += frames; + } + return n; +} + +static ssize_t snd_pcm_file_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_avail_update(file->slave); +} + +static int snd_pcm_file_mmap_status(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_mmap_status(file->slave, &pcm->mmap_status); +} + +static int snd_pcm_file_mmap_control(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_mmap_control(file->slave, &pcm->mmap_control); +} + +static int snd_pcm_file_mmap_data(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_mmap_data(file->slave, &pcm->mmap_data); +} + +static int snd_pcm_file_munmap_status(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_munmap_status(file->slave); +} + +static int snd_pcm_file_munmap_control(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_munmap_control(file->slave); +} + +static int snd_pcm_file_munmap_data(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_munmap_data(file->slave); +} + +static int snd_pcm_file_poll_descriptor(snd_pcm_t *pcm) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_poll_descriptor(file->slave); +} + +static int snd_pcm_file_channels_mask(snd_pcm_t *pcm, bitset_t *cmask) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_channels_mask(file->slave, cmask); +} + +static int snd_pcm_file_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_params_info(file->slave, info); +} + +static int snd_pcm_file_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_params(file->slave, params); +} + +static int snd_pcm_file_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_file_t *file = pcm->private; + return snd_pcm_setup(file->slave, setup); +} + +static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_file_t *file = pcm->private; + if (file->fname) + fprintf(fp, "File PCM (file=%s)\n", file->fname); + else + fprintf(fp, "File PCM (fd=%d)\n", file->fd); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(file->slave, fp); +} + +struct snd_pcm_ops snd_pcm_file_ops = { + close: snd_pcm_file_close, + info: snd_pcm_file_info, + params_info: snd_pcm_file_params_info, + params: snd_pcm_file_params, + setup: snd_pcm_file_setup, + channel_info: snd_pcm_file_channel_info, + channel_params: snd_pcm_file_channel_params, + channel_setup: snd_pcm_file_channel_setup, + dump: snd_pcm_file_dump, + nonblock: snd_pcm_file_nonblock, + mmap_status: snd_pcm_file_mmap_status, + mmap_control: snd_pcm_file_mmap_control, + mmap_data: snd_pcm_file_mmap_data, + munmap_status: snd_pcm_file_munmap_status, + munmap_control: snd_pcm_file_munmap_control, + munmap_data: snd_pcm_file_munmap_data, +}; + +struct snd_pcm_fast_ops snd_pcm_file_fast_ops = { + status: snd_pcm_file_status, + state: snd_pcm_file_state, + delay: snd_pcm_file_delay, + prepare: snd_pcm_file_prepare, + start: snd_pcm_file_start, + stop: snd_pcm_file_stop, + flush: snd_pcm_file_flush, + pause: snd_pcm_file_pause, + appl_ptr: snd_pcm_file_appl_ptr, + writei: snd_pcm_file_writei, + writen: snd_pcm_file_writen, + readi: snd_pcm_file_readi, + readn: snd_pcm_file_readn, + poll_descriptor: snd_pcm_file_poll_descriptor, + channels_mask: snd_pcm_file_channels_mask, + avail_update: snd_pcm_file_avail_update, + mmap_forward: snd_pcm_file_mmap_forward, +}; + +int snd_pcm_file_open(snd_pcm_t **handlep, char *fname, int fd, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_file_t *file; + int err; + assert(handlep && slave); + if (fname) { + fd = open(fname, O_WRONLY|O_CREAT, 0666); + if (fd < 0) + return -errno; + } + file = calloc(1, sizeof(snd_pcm_file_t)); + if (!file) { + return -ENOMEM; + } + file->fname = fname; + file->fd = fd; + file->slave = slave; + file->close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(file); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_FILE; + handle->stream = slave->stream; + handle->ops = &snd_pcm_file_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_file_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = file; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + char *fname = NULL; + long fd = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "file") == 0) { + err = snd_config_string_get(n, &fname); + if (err < 0) { + err = snd_config_integer_get(n, &fd); + if (err < 0) + return -EINVAL; + } + continue; + } + return -EINVAL; + } + if (!sname || (!fname && fd < 0)) + return -EINVAL; + if (fname) { + fname = strdup(fname); + if (!fname) + return -ENOMEM; + } + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_file_open(pcmp, fname, fd, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index 01d94291..d3adac9f 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -32,7 +32,7 @@ typedef struct { int fd; int card, device, subdevice; - void *mmap_data_ptr; + int mmap_emulation; } snd_pcm_hw_t; #define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip" @@ -100,6 +100,14 @@ static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) int fd = hw->fd; if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0) return -errno; + if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) { + if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED) + setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED; + else + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + hw->mmap_emulation = 1; + } else + hw->mmap_emulation = 0; return 0; } @@ -127,7 +135,18 @@ static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * se int fd = hw->fd; if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) return -errno; - setup->area.addr = (char *)hw->mmap_data_ptr + (long)setup->area.addr; + if (hw->mmap_emulation) { + if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) { + setup->area.addr = pcm->mmap_data; + setup->area.first = setup->channel * pcm->bits_per_sample; + setup->area.step = pcm->bits_per_frame; + } else { + setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; + setup->area.first = 0; + setup->area.step = pcm->bits_per_sample; + } + } else + setup->area.addr = (char *)pcm->mmap_data + (long)setup->area.addr; return 0; } @@ -145,19 +164,20 @@ static int snd_pcm_hw_state(snd_pcm_t *pcm) snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; snd_pcm_status_t status; - if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0) + if (pcm->mmap_status) + return pcm->mmap_status->state; + if (ioctl(fd, SND_PCM_IOCTL_STATUS, &status) < 0) return -errno; return status.state; } -static ssize_t snd_pcm_hw_hw_ptr(snd_pcm_t *pcm, int update ATTRIBUTE_UNUSED) +static int snd_pcm_hw_delay(snd_pcm_t *pcm, ssize_t *delayp) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - ssize_t pos = ioctl(fd, SND_PCM_IOCTL_HW_PTR); - if (pos < 0) + if (ioctl(fd, SND_PCM_IOCTL_DELAY, delayp) < 0) return -errno; - return pos; + return 0; } static int snd_pcm_hw_prepare(snd_pcm_t *pcm) @@ -169,20 +189,20 @@ static int snd_pcm_hw_prepare(snd_pcm_t *pcm) return 0; } -static int snd_pcm_hw_go(snd_pcm_t *pcm) +static int snd_pcm_hw_start(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_GO) < 0) + if (ioctl(fd, SND_PCM_IOCTL_START) < 0) return -errno; return 0; } -static int snd_pcm_hw_drain(snd_pcm_t *pcm) +static int snd_pcm_hw_stop(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0) + if (ioctl(fd, SND_PCM_IOCTL_STOP) < 0) return -errno; return 0; } @@ -218,79 +238,63 @@ static ssize_t snd_pcm_hw_appl_ptr(snd_pcm_t *pcm, off_t offset) return result; } -static ssize_t snd_pcm_hw_write(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size) +static ssize_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { ssize_t result; snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - snd_xfer_t xfer; - if (tstamp) - xfer.tstamp = *tstamp; - else - xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0; - xfer.buf = (char*) buffer; - xfer.count = size; - result = ioctl(fd, SND_PCM_IOCTL_WRITE_FRAMES, &xfer); + snd_xferi_t xferi; + xferi.buf = (char*) buffer; + xferi.frames = size; + result = ioctl(fd, SND_PCM_IOCTL_WRITEI_FRAMES, &xferi); if (result < 0) return -errno; return result; } -static ssize_t snd_pcm_hw_writev(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count) +static ssize_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, size_t size) { ssize_t result; snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - snd_xferv_t xferv; - if (tstamp) - xferv.tstamp = *tstamp; - else - xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0; - xferv.vector = vector; - xferv.count = count; - result = ioctl(fd, SND_PCM_IOCTL_WRITEV_FRAMES, &xferv); + snd_xfern_t xfern; + xfern.bufs = bufs; + xfern.frames = size; + result = ioctl(fd, SND_PCM_IOCTL_WRITEN_FRAMES, &xfern); if (result < 0) return -errno; return result; } -static ssize_t snd_pcm_hw_read(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size) +static ssize_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, size_t size) { ssize_t result; snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - snd_xfer_t xfer; - if (tstamp) - xfer.tstamp = *tstamp; - else - xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0; - xfer.buf = buffer; - xfer.count = size; - result = ioctl(fd, SND_PCM_IOCTL_READ_FRAMES, &xfer); + snd_xferi_t xferi; + xferi.buf = buffer; + xferi.frames = size; + result = ioctl(fd, SND_PCM_IOCTL_READI_FRAMES, &xferi); if (result < 0) return -errno; return result; } -ssize_t snd_pcm_hw_readv(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count) +ssize_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, size_t size) { ssize_t result; snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - snd_xferv_t xferv; - if (tstamp) - xferv.tstamp = *tstamp; - else - xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0; - xferv.vector = vector; - xferv.count = count; - result = ioctl(fd, SND_PCM_IOCTL_READV_FRAMES, &xferv); + snd_xfern_t xfern; + xfern.bufs = bufs; + xfern.frames = size; + result = ioctl(fd, SND_PCM_IOCTL_READN_FRAMES, &xfern); if (result < 0) return -errno; return result; } -static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status) +static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; void *ptr; @@ -298,11 +302,11 @@ static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status hw->fd, SND_PCM_MMAP_OFFSET_STATUS); if (ptr == MAP_FAILED || ptr == NULL) return -errno; - *status = ptr; + pcm->mmap_status = ptr; return 0; } -static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control) +static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; void *ptr; @@ -310,59 +314,97 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **cont hw->fd, SND_PCM_MMAP_OFFSET_CONTROL); if (ptr == MAP_FAILED || ptr == NULL) return -errno; - *control = ptr; + pcm->mmap_control = ptr; return 0; } -static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize) +static int snd_pcm_hw_mmap_data(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; void *ptr; - int prot; -#if 0 - prot = pcm->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ; -#else - prot = PROT_WRITE | PROT_READ; -#endif - ptr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED, - hw->fd, SND_PCM_MMAP_OFFSET_DATA); - if (ptr == MAP_FAILED || ptr == NULL) - return -errno; - *buffer = hw->mmap_data_ptr = ptr; + if (hw->mmap_emulation) { + ptr = malloc(snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size)); + if (!ptr) + return -ENOMEM; + } else { + int prot; + prot = PROT_WRITE | PROT_READ; + ptr = mmap(NULL, pcm->setup.mmap_bytes, + prot, MAP_FILE|MAP_SHARED, + hw->fd, SND_PCM_MMAP_OFFSET_DATA); + if (ptr == MAP_FAILED || ptr == NULL) + return -errno; + } + pcm->mmap_data = ptr; return 0; } -static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status) +static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm) { - if (munmap(status, sizeof(*status)) < 0) + if (munmap(pcm->mmap_status, sizeof(*pcm->mmap_status)) < 0) return -errno; return 0; } -static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control) +static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm) { - if (munmap(control, sizeof(*control)) < 0) + if (munmap(pcm->mmap_control, sizeof(*pcm->mmap_control)) < 0) return -errno; return 0; } -static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm, void *buffer, size_t bsize) +static int snd_pcm_hw_munmap_data(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; - if (munmap(buffer, bsize) < 0) - return -errno; - hw->mmap_data_ptr = NULL; + if (hw->mmap_emulation) + free(pcm->mmap_data); + else + if (munmap(pcm->mmap_data, pcm->setup.mmap_bytes) < 0) + return -errno; return 0; } -static int snd_pcm_hw_file_descriptor(snd_pcm_t *pcm) +static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size) +{ + snd_pcm_hw_t *hw = pcm->private; + if (hw->mmap_emulation && pcm->stream == SND_PCM_STREAM_PLAYBACK) + return snd_pcm_write_mmap(pcm, size); + snd_pcm_mmap_appl_forward(pcm, size); + return size; +} + +static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_hw_t *hw = pcm->private; + int fd = hw->fd; + size_t avail; + ssize_t err; + if (pcm->setup.ready_mode == SND_PCM_READY_ASAP) { + ssize_t d; + int err = ioctl(fd, SND_PCM_IOCTL_DELAY, &d); + if (err < 0) + return -errno; + } + avail = snd_pcm_mmap_avail(pcm); + if (avail > 0 && hw->mmap_emulation && + pcm->stream == SND_PCM_STREAM_CAPTURE) { + err = snd_pcm_read_mmap(pcm, avail); + if (err < 0) + return err; + assert((size_t)err == avail); + return err; + } + return avail; +} + +static int snd_pcm_hw_poll_descriptor(snd_pcm_t *pcm) { snd_pcm_hw_t *hw = pcm->private; return hw->fd; } static int snd_pcm_hw_channels_mask(snd_pcm_t *pcm ATTRIBUTE_UNUSED, - bitset_t *client_vmask ATTRIBUTE_UNUSED) + bitset_t *cmask ATTRIBUTE_UNUSED) { return 0; } @@ -387,35 +429,37 @@ struct snd_pcm_ops snd_pcm_hw_ops = { params_info: snd_pcm_hw_params_info, params: snd_pcm_hw_params, setup: snd_pcm_hw_setup, + channel_info: snd_pcm_hw_channel_info, + channel_params: snd_pcm_hw_channel_params, + channel_setup: snd_pcm_hw_channel_setup, dump: snd_pcm_hw_dump, + nonblock: snd_pcm_hw_nonblock, + mmap_status: snd_pcm_hw_mmap_status, + mmap_control: snd_pcm_hw_mmap_control, + mmap_data: snd_pcm_hw_mmap_data, + munmap_status: snd_pcm_hw_munmap_status, + munmap_control: snd_pcm_hw_munmap_control, + munmap_data: snd_pcm_hw_munmap_data, }; struct snd_pcm_fast_ops snd_pcm_hw_fast_ops = { - nonblock: snd_pcm_hw_nonblock, - channel_info: snd_pcm_hw_channel_info, - channel_params: snd_pcm_hw_channel_params, - channel_setup: snd_pcm_hw_channel_setup, status: snd_pcm_hw_status, - hw_ptr: snd_pcm_hw_hw_ptr, state: snd_pcm_hw_state, + delay: snd_pcm_hw_delay, prepare: snd_pcm_hw_prepare, - go: snd_pcm_hw_go, - drain: snd_pcm_hw_drain, + start: snd_pcm_hw_start, + stop: snd_pcm_hw_stop, flush: snd_pcm_hw_flush, pause: snd_pcm_hw_pause, appl_ptr: snd_pcm_hw_appl_ptr, - write: snd_pcm_hw_write, - writev: snd_pcm_hw_writev, - read: snd_pcm_hw_read, - readv: snd_pcm_hw_readv, - mmap_status: snd_pcm_hw_mmap_status, - mmap_control: snd_pcm_hw_mmap_control, - mmap_data: snd_pcm_hw_mmap_data, - munmap_status: snd_pcm_hw_munmap_status, - munmap_control: snd_pcm_hw_munmap_control, - munmap_data: snd_pcm_hw_munmap_data, - file_descriptor: snd_pcm_hw_file_descriptor, + writei: snd_pcm_hw_writei, + writen: snd_pcm_hw_writen, + readi: snd_pcm_hw_readi, + readn: snd_pcm_hw_readn, + poll_descriptor: snd_pcm_hw_poll_descriptor, channels_mask: snd_pcm_hw_channels_mask, + avail_update: snd_pcm_hw_avail_update, + mmap_forward: snd_pcm_hw_mmap_forward, }; int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int subdevice, int stream, int mode) @@ -432,7 +476,6 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub snd_pcm_hw_t *hw; assert(handlep); - *handlep = 0; if ((ret = snd_ctl_hw_open(&ctl, card)) < 0) return ret; @@ -482,14 +525,8 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub goto __again; } } - handle = calloc(1, sizeof(snd_pcm_t)); - if (!handle) { - ret = -ENOMEM; - goto __end; - } hw = calloc(1, sizeof(snd_pcm_hw_t)); - if (!handle) { - free(handle); + if (!hw) { ret = -ENOMEM; goto __end; } @@ -497,6 +534,13 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub hw->device = device; hw->subdevice = subdevice; hw->fd = fd; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(hw); + ret = -ENOMEM; + goto __end; + } handle->type = SND_PCM_TYPE_HW; handle->stream = stream; handle->ops = &snd_pcm_hw_ops; @@ -505,6 +549,12 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub handle->fast_op_arg = handle; handle->mode = mode; handle->private = hw; + ret = snd_pcm_init(handle); + if (ret < 0) { + snd_pcm_close(handle); + snd_ctl_close(ctl); + return ret; + } *handlep = handle; __end: @@ -514,8 +564,54 @@ int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int sub return ret; } -int snd_pcm_hw_open(snd_pcm_t **handlep, int card, int device, int stream, int mode) +int snd_pcm_hw_open_device(snd_pcm_t **handlep, int card, int device, int stream, int mode) { return snd_pcm_hw_open_subdevice(handlep, card, device, -1, stream, mode); } +int _snd_pcm_hw_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + long card = -1, device = 0, subdevice = -1; + char *str; + int err; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "card") == 0) { + err = snd_config_integer_get(n, &card); + if (err < 0) { + err = snd_config_string_get(n, &str); + if (err < 0) + return -EINVAL; + card = snd_card_get_index(str); + if (card < 0) + return card; + } + continue; + } + if (strcmp(n->id, "device") == 0) { + err = snd_config_integer_get(n, &device); + if (err < 0) + return err; + continue; + } + if (strcmp(n->id, "subdevice") == 0) { + err = snd_config_integer_get(n, &subdevice); + if (err < 0) + return err; + continue; + } + return -EINVAL; + } + if (card < 0) + return -EINVAL; + return snd_pcm_hw_open_subdevice(pcmp, card, device, subdevice, stream, mode); +} + diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c new file mode 100644 index 00000000..277be5b1 --- /dev/null +++ b/src/pcm/pcm_linear.c @@ -0,0 +1,360 @@ +/* + * PCM - Linear conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int conv_idx; + int sformat; + int cformat; + int cxfer_mode, cmmap_shape; +} snd_pcm_linear_t; + +static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset, + snd_pcm_channel_area_t *dst_areas, size_t dst_offset, + size_t frames, size_t channels, int convidx) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + void *conv = conv_labels[convidx]; + unsigned int channel; + for (channel = 0; channel < channels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static int snd_pcm_linear_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_linear_t *linear = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT && + !snd_pcm_format_linear(sfmt)) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + info->req.format.sfmt = linear->sformat; + err = snd_pcm_params_info(linear->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = SND_PCM_LINEAR_FORMATS; + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return err; +} + +static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_linear_t *linear = pcm->private; + snd_pcm_t *slave = linear->plug.slave; + int err; + if (!snd_pcm_format_linear(params->format.sfmt)) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + linear->cformat = params->format.sfmt; + linear->cxfer_mode = params->xfer_mode; + linear->cmmap_shape = params->mmap_shape; + params->format.sfmt = linear->sformat; + params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; + params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED; + err = snd_pcm_params(slave, params); + params->format.sfmt = linear->cformat; + params->xfer_mode = linear->cxfer_mode; + params->mmap_shape = linear->cmmap_shape; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_linear_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_linear_t *linear = pcm->private; + int err = snd_pcm_setup(linear->plug.slave, setup); + if (err < 0) + return err; + assert(linear->sformat == setup->format.sfmt); + + if (linear->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = linear->cxfer_mode; + if (linear->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = linear->cmmap_shape; + setup->format.sfmt = linear->cformat; + setup->mmap_bytes = 0; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + linear->conv_idx = conv_index(linear->cformat, + linear->sformat); + else + linear->conv_idx = conv_index(linear->sformat, + linear->cformat); + return 0; +} + +static ssize_t snd_pcm_linear_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_linear_t *linear = pcm->private; + snd_pcm_t *slave = linear->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + linear_transfer(areas, offset, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + frames, pcm->setup.format.channels, linear->conv_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static ssize_t snd_pcm_linear_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_linear_t *linear = pcm->private; + snd_pcm_t *slave = linear->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + linear_transfer(slave->mmap_areas, snd_pcm_mmap_offset(slave), + areas, offset, + frames, pcm->setup.format.channels, linear->conv_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_linear_t *linear = pcm->private; + fprintf(fp, "Linear conversion PCM (%s)\n", + snd_pcm_format_name(linear->sformat)); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(linear->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_linear_ops = { + close: snd_pcm_plugin_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_linear_params_info, + params: snd_pcm_linear_params, + setup: snd_pcm_linear_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_plugin_channel_setup, + dump: snd_pcm_linear_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int snd_pcm_linear_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_linear_t *linear; + int err; + assert(handlep && slave); + if (snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + linear = calloc(1, sizeof(snd_pcm_linear_t)); + if (!linear) { + return -ENOMEM; + } + linear->sformat = sformat; + linear->plug.read = snd_pcm_linear_read_areas; + linear->plug.write = snd_pcm_linear_write_areas; + linear->plug.slave = slave; + linear->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(linear); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_LINEAR; + handle->stream = slave->stream; + handle->ops = &snd_pcm_linear_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = linear; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_linear_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname || !sformat) + return -EINVAL; + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_linear_open(pcmp, sformat, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 8cabad13..35154c7a 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -20,43 +20,49 @@ */ #include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/uio.h> +#include <errno.h> #include "asoundlib.h" struct snd_pcm_ops { int (*close)(snd_pcm_t *pcm); + int (*nonblock)(snd_pcm_t *pcm, int nonblock); int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info); int (*params_info)(snd_pcm_t *pcm, snd_pcm_params_info_t *info); int (*params)(snd_pcm_t *pcm, snd_pcm_params_t *params); int (*setup)(snd_pcm_t *pcm, snd_pcm_setup_t *setup); + 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); void (*dump)(snd_pcm_t *pcm, FILE *fp); + int (*mmap_status)(snd_pcm_t *pcm); + int (*mmap_control)(snd_pcm_t *pcm); + int (*mmap_data)(snd_pcm_t *pcm); + int (*munmap_status)(snd_pcm_t *pcm); + int (*munmap_control)(snd_pcm_t *pcm); + int (*munmap_data)(snd_pcm_t *pcm); }; struct snd_pcm_fast_ops { - int (*nonblock)(snd_pcm_t *pcm, int nonblock); int (*status)(snd_pcm_t *pcm, snd_pcm_status_t *status); - 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 (*prepare)(snd_pcm_t *pcm); - int (*go)(snd_pcm_t *pcm); - int (*drain)(snd_pcm_t *pcm); + int (*start)(snd_pcm_t *pcm); + int (*stop)(snd_pcm_t *pcm); int (*flush)(snd_pcm_t *pcm); int (*pause)(snd_pcm_t *pcm, int enable); int (*state)(snd_pcm_t *pcm); - ssize_t (*hw_ptr)(snd_pcm_t *pcm, int update); + int (*delay)(snd_pcm_t *pcm, ssize_t *delayp); ssize_t (*appl_ptr)(snd_pcm_t *pcm, off_t offset); - ssize_t (*write)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const void *buffer, size_t size); - ssize_t (*writev)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count); - ssize_t (*read)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, void *buffer, size_t size); - ssize_t (*readv)(snd_pcm_t *pcm, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count); - int (*file_descriptor)(snd_pcm_t *pcm); - int (*channels_mask)(snd_pcm_t *pcm, bitset_t *client_vmask); - int (*mmap_status)(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status); - int (*mmap_control)(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control); - int (*mmap_data)(snd_pcm_t *pcm, void **buffer, size_t bsize); - int (*munmap_status)(snd_pcm_t *pcm, snd_pcm_mmap_status_t *status); - int (*munmap_control)(snd_pcm_t *pcm, snd_pcm_mmap_control_t *control); - int (*munmap_data)(snd_pcm_t *pcm, void *buffer, size_t bsize); + ssize_t (*writei)(snd_pcm_t *pcm, const void *buffer, size_t size); + ssize_t (*writen)(snd_pcm_t *pcm, void **bufs, size_t size); + ssize_t (*readi)(snd_pcm_t *pcm, void *buffer, size_t size); + ssize_t (*readn)(snd_pcm_t *pcm, void **bufs, size_t size); + int (*poll_descriptor)(snd_pcm_t *pcm); + int (*channels_mask)(snd_pcm_t *pcm, bitset_t *cmask); + ssize_t (*avail_update)(snd_pcm_t *pcm); + ssize_t (*mmap_forward)(snd_pcm_t *pcm, size_t size); }; struct snd_pcm { @@ -65,13 +71,12 @@ struct snd_pcm { int mode; int valid_setup; snd_pcm_setup_t setup; - snd_pcm_channel_area_t *channels; size_t bits_per_sample; size_t bits_per_frame; snd_pcm_mmap_status_t *mmap_status; snd_pcm_mmap_control_t *mmap_control; - char *mmap_data; - enum { _INTERLEAVED, _NONINTERLEAVED, _COMPLEX } mmap_type; + void *mmap_data; + snd_pcm_channel_area_t *mmap_areas; struct snd_pcm_ops *ops; struct snd_pcm_fast_ops *fast_ops; snd_pcm_t *op_arg; @@ -79,74 +84,67 @@ struct snd_pcm { void *private; }; -#undef snd_pcm_plug_t -typedef struct snd_pcm_plug { - int close_slave; - snd_pcm_t *handle; - snd_pcm_t *slave; - snd_pcm_plugin_t *first; - snd_pcm_plugin_t *last; - size_t frames_alloc; -} snd_pcm_plug_t; - -unsigned int snd_pcm_plug_formats(unsigned int slave_formats); -int snd_pcm_plug_slave_fmt(int format, snd_pcm_params_info_t *slave_info); -int snd_pcm_plug_slave_rate(unsigned int rate, snd_pcm_params_info_t *slave_info); -int snd_pcm_plug_slave_format(snd_pcm_format_t *format, - snd_pcm_info_t *slave_info, - snd_pcm_params_info_t *slave_params_info, - snd_pcm_format_t *slave_format); -int snd_pcm_plug_format_plugins(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_format_t *slave_format); +int snd_pcm_init(snd_pcm_t *pcm); +void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf); +void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs); -ssize_t snd_pcm_plug_write_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *src_channels, size_t size); -ssize_t snd_pcm_plug_read_transfer(snd_pcm_plug_t *plug, snd_pcm_plugin_channel_t *dst_channels_final, size_t size); -ssize_t snd_pcm_plug_client_channels_iovec(snd_pcm_plug_t *plug, - const struct iovec *vector, unsigned long count, - snd_pcm_plugin_channel_t **channels); -ssize_t snd_pcm_plug_client_channels_buf(snd_pcm_plug_t *plug, - char *buf, size_t count, - snd_pcm_plugin_channel_t **channels); +int snd_pcm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status); +int snd_pcm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control); +int snd_pcm_mmap_data(snd_pcm_t *pcm, void **buffer); +int snd_pcm_munmap_status(snd_pcm_t *pcm); +int snd_pcm_munmap_control(snd_pcm_t *pcm); +int snd_pcm_munmap_data(snd_pcm_t *pcm); +int snd_pcm_mmap_ready(snd_pcm_t *pcm); +ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset); +void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames); +void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames); +size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm); +size_t snd_pcm_mmap_avail(snd_pcm_t *pcm); +size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames); +size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames); -int snd_pcm_plug_playback_channels_mask(snd_pcm_plug_t *plug, - bitset_t *client_vmask); -int snd_pcm_plug_capture_channels_mask(snd_pcm_plug_t *plug, - bitset_t *client_vmask); -ssize_t snd_pcm_plugin_client_channels(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels); +typedef ssize_t (*snd_pcm_xfer_areas_func_t)(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, size_t size, + size_t *slave_sizep); -void *snd_pcm_plug_buf_alloc(snd_pcm_plug_t *plug, size_t size); -void snd_pcm_plug_buf_unlock(snd_pcm_plug_t *pcm, void *ptr); +ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + size_t offset, size_t size, + snd_pcm_xfer_areas_func_t func); +ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, + size_t offset, size_t size, + snd_pcm_xfer_areas_func_t func); +ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size); +ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size); -#define ROUTE_PLUGIN_RESOLUTION 16 - -int getput_index(int format); -int conv_index(int src_format, int dst_format); - -#ifdef PLUGIN_DEBUG -#define pdprintf( args... ) fprintf(stderr, "plugin: " ##args) -#else -#define pdprintf( args... ) { ; } -#endif - -static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *str) +static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm) { ssize_t avail; - avail = str->mmap_status->hw_ptr + str->setup.buffer_size - str->mmap_control->appl_ptr; + avail = pcm->mmap_status->hw_ptr + pcm->setup.buffer_size - pcm->mmap_control->appl_ptr; if (avail < 0) - avail += str->setup.boundary; + avail += pcm->setup.boundary; return avail; } -static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *str) +static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm) { ssize_t avail; - avail = str->mmap_status->hw_ptr - str->mmap_control->appl_ptr; + avail = pcm->mmap_status->hw_ptr - pcm->mmap_control->appl_ptr; if (avail < 0) - avail += str->setup.boundary; + avail += pcm->setup.boundary; return avail; } -#define snd_pcm_plug_stream(plug) ((plug)->handle->stream) +static inline void *snd_pcm_channel_area_addr(snd_pcm_channel_area_t *area, size_t offset) +{ + size_t bitofs = area->first + area->step * offset; + assert(bitofs % 8 == 0); + return area->addr + bitofs / 8; +} + +static inline size_t snd_pcm_channel_area_step(snd_pcm_channel_area_t *area) +{ + assert(area->step % 8 == 0); + return area->step / 8; +} + diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index b829f5c9..57bfcf21 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -23,6 +23,9 @@ #include "../include/driver.h" #include "../include/pcm.h" #include "../include/pcm_plugin.h" +#define bswap_16 swab16 +#define bswap_32 swab32 +#define bswap_64 swab64 #else #include <stdio.h> #include <stdlib.h> diff --git a/src/pcm/pcm_mmap.c b/src/pcm/pcm_mmap.c index fdb5a08e..2be47c53 100644 --- a/src/pcm/pcm_mmap.c +++ b/src/pcm/pcm_mmap.c @@ -26,461 +26,313 @@ #include <sys/uio.h> #include "pcm_local.h" -int snd_pcm_avail(snd_pcm_t *handle, ssize_t *frames) +size_t snd_pcm_mmap_avail(snd_pcm_t *pcm) { - assert(handle); - assert(handle->mmap_status && handle->mmap_control); - if (handle->stream == SND_PCM_STREAM_PLAYBACK) - *frames = snd_pcm_mmap_playback_avail(handle); + assert(pcm); + assert(pcm->mmap_status && pcm->mmap_control); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + return snd_pcm_mmap_playback_avail(pcm); else - *frames = snd_pcm_mmap_capture_avail(handle); + return snd_pcm_mmap_capture_avail(pcm); return 0; } -static int snd_pcm_mmap_playback_ready(snd_pcm_t *handle) +static int snd_pcm_mmap_playback_ready(snd_pcm_t *pcm) { - if (handle->mmap_status->state == SND_PCM_STATE_XRUN) + if (pcm->mmap_status->state == SND_PCM_STATE_XRUN) return -EPIPE; - return snd_pcm_mmap_playback_avail(handle) >= handle->setup.avail_min; + return snd_pcm_mmap_playback_avail(pcm) >= pcm->setup.avail_min; } -static int snd_pcm_mmap_capture_ready(snd_pcm_t *handle) +static int snd_pcm_mmap_capture_ready(snd_pcm_t *pcm) { int ret = 0; - if (handle->mmap_status->state == SND_PCM_STATE_XRUN) { + if (pcm->mmap_status->state == SND_PCM_STATE_XRUN) { ret = -EPIPE; - if (handle->setup.xrun_mode == SND_PCM_XRUN_DRAIN) + if (pcm->setup.xrun_act == SND_PCM_XRUN_ACT_DRAIN) return -EPIPE; } - if (snd_pcm_mmap_capture_avail(handle) >= handle->setup.avail_min) + if (snd_pcm_mmap_capture_avail(pcm) >= pcm->setup.avail_min) return 1; return ret; } -int snd_pcm_mmap_ready(snd_pcm_t *handle) +int snd_pcm_mmap_ready(snd_pcm_t *pcm) { - assert(handle); - assert(handle->mmap_status && handle->mmap_control); - assert(handle->mmap_status->state >= SND_PCM_STATE_PREPARED); - if (handle->stream == SND_PCM_STREAM_PLAYBACK) { - return snd_pcm_mmap_playback_ready(handle); + assert(pcm); + assert(pcm->mmap_status && pcm->mmap_control); + assert(pcm->mmap_status->state >= SND_PCM_STATE_PREPARED); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + return snd_pcm_mmap_playback_ready(pcm); } else { - return snd_pcm_mmap_capture_ready(handle); + return snd_pcm_mmap_capture_ready(pcm); } } -static size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *handle, size_t frames) +size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames) { - snd_pcm_mmap_control_t *control = handle->mmap_control; + snd_pcm_mmap_control_t *control = pcm->mmap_control; size_t cont; - size_t avail = snd_pcm_mmap_playback_avail(handle); + size_t avail = snd_pcm_mmap_playback_avail(pcm); if (avail < frames) frames = avail; - cont = handle->setup.buffer_size - control->appl_ptr % handle->setup.buffer_size; + cont = pcm->setup.buffer_size - control->appl_ptr % pcm->setup.buffer_size; if (cont < frames) frames = cont; return frames; } -static size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *handle, size_t frames) +size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames) { - snd_pcm_mmap_control_t *control = handle->mmap_control; + snd_pcm_mmap_control_t *control = pcm->mmap_control; size_t cont; - size_t avail = snd_pcm_mmap_capture_avail(handle); + size_t avail = snd_pcm_mmap_capture_avail(pcm); if (avail < frames) frames = avail; - cont = handle->setup.buffer_size - control->appl_ptr % handle->setup.buffer_size; + cont = pcm->setup.buffer_size - control->appl_ptr % pcm->setup.buffer_size; if (cont < frames) frames = cont; return frames; } -ssize_t snd_pcm_mmap_xfer(snd_pcm_t *handle, size_t frames) +size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t frames) { - assert(handle); - assert(handle->mmap_status && handle->mmap_control); - if (handle->stream == SND_PCM_STREAM_PLAYBACK) - return snd_pcm_mmap_playback_xfer(handle, frames); + assert(pcm); + assert(pcm->mmap_status && pcm->mmap_control); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + return snd_pcm_mmap_playback_xfer(pcm, frames); else - return snd_pcm_mmap_capture_xfer(handle, frames); + return snd_pcm_mmap_capture_xfer(pcm, frames); } -ssize_t snd_pcm_mmap_offset(snd_pcm_t *handle) +size_t snd_pcm_mmap_offset(snd_pcm_t *pcm) { - assert(handle); - assert(handle->mmap_control); - return handle->mmap_control->appl_ptr % handle->setup.buffer_size; + assert(pcm); + assert(pcm->mmap_control); + return pcm->mmap_control->appl_ptr % pcm->setup.buffer_size; } -int snd_pcm_mmap_state(snd_pcm_t *handle) +size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm) { - assert(handle); - assert(handle->mmap_status); - return handle->mmap_status->state; + assert(pcm); + assert(pcm->mmap_status); + return pcm->mmap_status->hw_ptr % pcm->setup.buffer_size; } -ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *handle) +int snd_pcm_mmap_state(snd_pcm_t *pcm) { - assert(handle); - assert(handle->mmap_status); - return handle->mmap_status->hw_ptr; + assert(pcm); + assert(pcm->mmap_status); + return pcm->mmap_status->state; } -ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *handle, off_t offset) +ssize_t snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm) +{ + assert(pcm); + assert(pcm->mmap_status); + return pcm->mmap_status->hw_ptr; +} + +ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset) { ssize_t appl_ptr; - assert(handle); - assert(handle->mmap_status && handle->mmap_control); - assert(offset == 0 || handle->type == SND_PCM_TYPE_HW); - appl_ptr = handle->mmap_control->appl_ptr; + assert(pcm); + assert(pcm->mmap_status && pcm->mmap_control); + assert(offset == 0 || pcm->type == SND_PCM_TYPE_HW); + appl_ptr = pcm->mmap_control->appl_ptr; if (offset == 0) return appl_ptr; - switch (handle->mmap_status->state) { + switch (pcm->mmap_status->state) { case SND_PCM_STATE_RUNNING: - if (handle->setup.mode == SND_PCM_MODE_FRAME) - snd_pcm_hw_ptr(handle, 1); + if (pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) + snd_pcm_avail_update(pcm); break; case SND_PCM_STATE_READY: case SND_PCM_STATE_NOTREADY: return -EBADFD; } if (offset < 0) { - if (offset < -(ssize_t)handle->setup.buffer_size) - offset = -(ssize_t)handle->setup.buffer_size; - else - offset -= offset % handle->setup.align; + if (offset < -(ssize_t)pcm->setup.buffer_size) + offset = -(ssize_t)pcm->setup.buffer_size; appl_ptr += offset; if (appl_ptr < 0) - appl_ptr += handle->setup.boundary; + appl_ptr += pcm->setup.boundary; } else { size_t avail; - if (handle->stream == SND_PCM_STREAM_PLAYBACK) - avail = snd_pcm_mmap_playback_avail(handle); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + avail = snd_pcm_mmap_playback_avail(pcm); else - avail = snd_pcm_mmap_capture_avail(handle); + avail = snd_pcm_mmap_capture_avail(pcm); if ((size_t)offset > avail) offset = avail; - offset -= offset % handle->setup.align; appl_ptr += offset; - if ((size_t)appl_ptr >= handle->setup.boundary) - appl_ptr -= handle->setup.boundary; + if ((size_t)appl_ptr >= pcm->setup.boundary) + appl_ptr -= pcm->setup.boundary; } - handle->mmap_control->appl_ptr = appl_ptr; + pcm->mmap_control->appl_ptr = appl_ptr; return appl_ptr; } -ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames) +void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames) { - snd_pcm_mmap_status_t *status; - size_t offset = 0; - size_t result = 0; - int err; - - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - status = handle->mmap_status; - assert(status->state >= SND_PCM_STATE_PREPARED); - if (handle->setup.mode == SND_PCM_MODE_FRAGMENT) { - assert(frames % handle->setup.frag_size == 0); - } else { - if (status->state == SND_PCM_STATE_RUNNING && - handle->mode & SND_PCM_NONBLOCK) - snd_pcm_hw_ptr(handle, 1); - } - while (frames > 0) { - ssize_t mmap_offset; - size_t frames1; - int ready = snd_pcm_mmap_playback_ready(handle); - if (ready < 0) - return ready; - if (!ready) { - struct pollfd pfd; - if (status->state != SND_PCM_STATE_RUNNING) - return result > 0 ? result : -EPIPE; - if (handle->mode & SND_PCM_NONBLOCK) - return result > 0 ? result : -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(handle); - 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(handle)); - } - frames1 = snd_pcm_mmap_playback_xfer(handle, frames); - assert(frames1 > 0); - mmap_offset = snd_pcm_mmap_offset(handle); - snd_pcm_areas_copy(channels, offset, handle->channels, mmap_offset, handle->setup.format.channels, frames1, handle->setup.format.format); - if (status->state == SND_PCM_STATE_XRUN) - return result > 0 ? result : -EPIPE; - snd_pcm_appl_ptr(handle, frames1); - frames -= frames1; - offset += frames1; - result += frames1; - if (status->state == SND_PCM_STATE_PREPARED && - (handle->setup.start_mode == SND_PCM_START_DATA || - (handle->setup.start_mode == SND_PCM_START_FULL && - !snd_pcm_mmap_playback_ready(handle)))) { - err = snd_pcm_go(handle); - if (err < 0) - return result > 0 ? result : err; - } - } - return result; + size_t appl_ptr = pcm->mmap_control->appl_ptr; + appl_ptr += frames; + if (appl_ptr >= pcm->setup.boundary) + appl_ptr -= pcm->setup.boundary; + pcm->mmap_control->appl_ptr = appl_ptr; } -ssize_t snd_pcm_mmap_write(snd_pcm_t *handle, const void *buffer, size_t frames) +void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames) { - unsigned int nchannels; - assert(handle); - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - assert(frames == 0 || buffer); - nchannels = handle->setup.format.channels; - assert(handle->setup.format.interleave || nchannels == 1); - { - snd_pcm_channel_area_t channels[nchannels]; - unsigned int channel; - for (channel = 0; channel < nchannels; ++channel) { - channels[channel].addr = (char*)buffer; - channels[channel].first = handle->bits_per_sample * channel; - channels[channel].step = handle->bits_per_frame; - } - return snd_pcm_mmap_write_areas(handle, channels, frames); - } + size_t hw_ptr = pcm->mmap_status->hw_ptr; + hw_ptr += frames; + if (hw_ptr >= pcm->setup.boundary) + hw_ptr -= pcm->setup.boundary; + pcm->mmap_status->hw_ptr = hw_ptr; } -ssize_t snd_pcm_mmap_writev(snd_pcm_t *handle, const struct iovec *vector, unsigned long vcount) +ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) { - size_t result = 0; - unsigned int nchannels; - assert(handle); - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - assert(vcount == 0 || vector); - nchannels = handle->setup.format.channels; - if (handle->setup.format.interleave) { - unsigned int b; - for (b = 0; b < vcount; b++) { - ssize_t ret; - size_t frames = vector[b].iov_len; - ret = snd_pcm_mmap_write(handle, vector[b].iov_base, frames); - if (ret < 0) { - if (result <= 0) - return ret; - break; - } - result += ret; - } - } else { - snd_pcm_channel_area_t channels[nchannels]; - unsigned long bcount; - unsigned int b; - assert(vcount % nchannels == 0); - bcount = vcount / nchannels; - for (b = 0; b < bcount; b++) { - unsigned int v; - ssize_t ret; - size_t frames = vector[0].iov_len; - for (v = 0; v < nchannels; ++v) { - assert(vector[v].iov_len == frames); - channels[v].addr = vector[v].iov_base; - channels[v].first = 0; - channels[v].step = handle->bits_per_sample; - } - ret = snd_pcm_mmap_write_areas(handle, channels, frames); - if (ret < 0) { - if (result <= 0) - return ret; - break; - } - result += ret; - if ((size_t)ret != frames) - break; - vector += nchannels; - } + size_t xfer; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + xfer = 0; + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(pcm, size - xfer); + snd_pcm_areas_copy(areas, offset, + pcm->mmap_areas, snd_pcm_mmap_offset(pcm), + pcm->setup.format.channels, + frames, pcm->setup.format.sfmt); + err = snd_pcm_mmap_forward(pcm, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; } - return result; + return err; } -ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *channels, size_t frames) +ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) { - snd_pcm_mmap_status_t *status; - size_t offset = 0; - size_t result = 0; - int err; - - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - status = handle->mmap_status; - assert(status->state >= SND_PCM_STATE_PREPARED); - if (handle->setup.mode == SND_PCM_MODE_FRAGMENT) { - assert(frames % handle->setup.frag_size == 0); - } else { - if (status->state == SND_PCM_STATE_RUNNING && - handle->mode & SND_PCM_NONBLOCK) - snd_pcm_hw_ptr(handle, 1); - } - if (status->state == SND_PCM_STATE_PREPARED && - handle->setup.start_mode == SND_PCM_START_DATA) { - err = snd_pcm_go(handle); + size_t xfer; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + xfer = 0; + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(pcm, size - xfer); + snd_pcm_areas_copy(pcm->mmap_areas, snd_pcm_mmap_offset(pcm), + areas, offset, + pcm->setup.format.channels, + frames, pcm->setup.format.sfmt); + err = snd_pcm_mmap_forward(pcm, frames); if (err < 0) - return err; + break; + assert((size_t)err == frames); + offset += err; + xfer += err; } - while (frames > 0) { - ssize_t mmap_offset; - size_t frames1; - int ready = snd_pcm_mmap_capture_ready(handle); - if (ready < 0) - return ready; - if (!ready) { - struct pollfd pfd; - if (status->state != SND_PCM_STATE_RUNNING) - return result > 0 ? result : -EPIPE; - if (handle->mode & SND_PCM_NONBLOCK) - return result > 0 ? result : -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(handle); - 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(handle)); - } - frames1 = snd_pcm_mmap_capture_xfer(handle, frames); - assert(frames1 > 0); - mmap_offset = snd_pcm_mmap_offset(handle); - snd_pcm_areas_copy(handle->channels, mmap_offset, channels, offset, handle->setup.format.channels, frames1, handle->setup.format.format); - if (status->state == SND_PCM_STATE_XRUN && - handle->setup.xrun_mode == SND_PCM_XRUN_DRAIN) - return result > 0 ? result : -EPIPE; - snd_pcm_appl_ptr(handle, frames1); - frames -= frames1; - offset += frames1; - result += frames1; + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; } - return result; + return err; } -ssize_t snd_pcm_mmap_read(snd_pcm_t *handle, void *buffer, size_t frames) +ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size) { - unsigned int nchannels; - assert(handle); - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - assert(frames == 0 || buffer); - nchannels = handle->setup.format.channels; - assert(handle->setup.format.interleave || nchannels == 1); - { - snd_pcm_channel_area_t channels[nchannels]; - unsigned int channel; - for (channel = 0; channel < nchannels; ++channel) { - channels[channel].addr = (char*)buffer; - channels[channel].first = handle->bits_per_sample * channel; - channels[channel].step = handle->bits_per_frame; - } - return snd_pcm_mmap_read_areas(handle, channels, frames); - } + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); + return snd_pcm_write_areas(pcm, areas, 0, size, + snd_pcm_mmap_write_areas); } -ssize_t snd_pcm_mmap_readv(snd_pcm_t *handle, const struct iovec *vector, unsigned long vcount) +ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size) { - size_t result = 0; - unsigned int nchannels; - assert(handle); - assert(handle->mmap_data && handle->mmap_status && handle->mmap_control); - assert(vcount == 0 || vector); - nchannels = handle->setup.format.channels; - if (handle->setup.format.interleave) { - unsigned int b; - for (b = 0; b < vcount; b++) { - ssize_t ret; - size_t frames = vector[b].iov_len; - ret = snd_pcm_mmap_read(handle, vector[b].iov_base, frames); - if (ret < 0) { - if (result <= 0) - return ret; - break; - } - result += ret; - } - } else { - snd_pcm_channel_area_t channels[nchannels]; - unsigned long bcount; - unsigned int b; - assert(vcount % nchannels == 0); - bcount = vcount / nchannels; - for (b = 0; b < bcount; b++) { - unsigned int v; - ssize_t ret; - size_t frames = vector[0].iov_len; - for (v = 0; v < nchannels; ++v) { - assert(vector[v].iov_len == frames); - channels[v].addr = vector[v].iov_base; - channels[v].first = 0; - channels[v].step = handle->bits_per_sample; - } - ret = snd_pcm_mmap_read_areas(handle, channels, frames); - if (ret < 0) { - if (result <= 0) - return ret; - break; - } - result += ret; - if ((size_t)ret != frames) - break; - vector += nchannels; - } - } - return result; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_areas_from_bufs(pcm, areas, bufs); + return snd_pcm_write_areas(pcm, areas, 0, size, + snd_pcm_mmap_write_areas); } -int snd_pcm_mmap_status(snd_pcm_t *handle, snd_pcm_mmap_status_t **status) +ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size) +{ + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_areas_from_buf(pcm, areas, buffer); + return snd_pcm_read_areas(pcm, areas, 0, size, + snd_pcm_mmap_read_areas); +} + +ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size) +{ + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + snd_pcm_areas_from_bufs(pcm, areas, bufs); + return snd_pcm_read_areas(pcm, areas, 0, size, + snd_pcm_mmap_read_areas); +} + +int snd_pcm_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status) { int err; - assert(handle); - assert(handle->valid_setup); - if (handle->mmap_status) { + assert(pcm); + if (pcm->mmap_status) { if (status) - *status = handle->mmap_status; + *status = pcm->mmap_status; return 0; } - if ((err = handle->fast_ops->mmap_status(handle->fast_op_arg, &handle->mmap_status)) < 0) + if ((err = pcm->ops->mmap_status(pcm->op_arg)) < 0) return err; if (status) - *status = handle->mmap_status; + *status = pcm->mmap_status; return 0; } -int snd_pcm_mmap_control(snd_pcm_t *handle, snd_pcm_mmap_control_t **control) +int snd_pcm_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control) { int err; - assert(handle); - assert(handle->valid_setup); - if (handle->mmap_control) { + assert(pcm); + if (pcm->mmap_control) { if (control) - *control = handle->mmap_control; + *control = pcm->mmap_control; return 0; } - if ((err = handle->fast_ops->mmap_control(handle->fast_op_arg, &handle->mmap_control)) < 0) + if ((err = pcm->ops->mmap_control(pcm->op_arg)) < 0) return err; if (control) - *control = handle->mmap_control; + *control = pcm->mmap_control; return 0; } -int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas) +int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas) { snd_pcm_channel_setup_t s; snd_pcm_channel_area_t *a, *ap; unsigned int channel; - int interleaved = 1, noninterleaved = 1; int err; - assert(handle); - assert(handle->mmap_data); - a = calloc(handle->setup.format.channels, sizeof(*areas)); - for (channel = 0, ap = a; channel < handle->setup.format.channels; ++channel, ++ap) { + assert(pcm); + assert(pcm->mmap_data); + a = calloc(pcm->setup.format.channels, sizeof(*areas)); + for (channel = 0, ap = a; channel < pcm->setup.format.channels; ++channel, ++ap) { s.channel = channel; - err = snd_pcm_channel_setup(handle, &s); + err = snd_pcm_channel_setup(pcm, &s); if (err < 0) { free(a); return err; @@ -488,110 +340,146 @@ int snd_pcm_mmap_get_areas(snd_pcm_t *handle, snd_pcm_channel_area_t *areas) if (areas) areas[channel] = s.area; *ap = s.area; - if (ap->step != handle->bits_per_sample || ap->first != 0) - noninterleaved = 0; - if (ap->addr != a[0].addr || - ap->step != handle->bits_per_frame || - ap->first != channel * handle->bits_per_sample) - interleaved = 0; } - if (noninterleaved) - handle->mmap_type = _NONINTERLEAVED; - else if (interleaved) - handle->mmap_type = _INTERLEAVED; - else - handle->mmap_type = _COMPLEX; - handle->channels = a; + pcm->mmap_areas = a; return 0; } -int snd_pcm_mmap_data(snd_pcm_t *handle, void **data) +int snd_pcm_mmap_data(snd_pcm_t *pcm, void **data) { int err; - assert(handle); - assert(handle->valid_setup); - if (handle->mmap_data) { + assert(pcm); + assert(pcm->valid_setup); + if (pcm->mmap_data) { if (data) - *data = handle->mmap_data; + *data = pcm->mmap_data; return 0; } - if (handle->setup.mmap_bytes == 0) - return -ENXIO; - if ((err = handle->fast_ops->mmap_data(handle->fast_op_arg, (void**)&handle->mmap_data, handle->setup.mmap_bytes)) < 0) + if ((err = pcm->ops->mmap_data(pcm->op_arg)) < 0) return err; if (data) - *data = handle->mmap_data; - err = snd_pcm_mmap_get_areas(handle, NULL); + *data = pcm->mmap_data; + err = snd_pcm_mmap_get_areas(pcm, NULL); if (err < 0) return err; return 0; } -int snd_pcm_mmap(snd_pcm_t *handle, snd_pcm_mmap_status_t **status, snd_pcm_mmap_control_t **control, void **data) +int snd_pcm_munmap_status(snd_pcm_t *pcm) { int err; - err = snd_pcm_mmap_status(handle, status); - if (err < 0) + assert(pcm); + assert(pcm->mmap_status); + if ((err = pcm->ops->munmap_status(pcm->op_arg)) < 0) return err; - err = snd_pcm_mmap_control(handle, control); - if (err < 0) { - snd_pcm_munmap_status(handle); - return err; - } - err = snd_pcm_mmap_data(handle, data); - if (err < 0) { - snd_pcm_munmap_status(handle); - snd_pcm_munmap_control(handle); - return err; - } + pcm->mmap_status = 0; return 0; } -int snd_pcm_munmap_status(snd_pcm_t *handle) +int snd_pcm_munmap_control(snd_pcm_t *pcm) { int err; - assert(handle); - assert(handle->mmap_status); - if ((err = handle->fast_ops->munmap_status(handle->fast_op_arg, handle->mmap_status)) < 0) + assert(pcm); + assert(pcm->mmap_control); + if ((err = pcm->ops->munmap_control(pcm->op_arg)) < 0) return err; - handle->mmap_status = 0; + pcm->mmap_control = 0; return 0; } -int snd_pcm_munmap_control(snd_pcm_t *handle) +int snd_pcm_munmap_data(snd_pcm_t *pcm) { int err; - assert(handle); - assert(handle->mmap_control); - if ((err = handle->fast_ops->munmap_control(handle->fast_op_arg, handle->mmap_control)) < 0) + assert(pcm); + assert(pcm->mmap_data); + if ((err = pcm->ops->munmap_data(pcm->op_arg)) < 0) return err; - handle->mmap_control = 0; + free(pcm->mmap_areas); + pcm->mmap_areas = 0; + pcm->mmap_data = 0; return 0; } -int snd_pcm_munmap_data(snd_pcm_t *handle) +int snd_pcm_mmap(snd_pcm_t *pcm, void **data) { - int err; - assert(handle); - assert(handle->mmap_data); - if ((err = handle->fast_ops->munmap_data(handle->fast_op_arg, handle->mmap_data, handle->setup.mmap_bytes)) < 0) - return err; - free(handle->channels); - handle->channels = 0; - handle->mmap_data = 0; - return 0; + return snd_pcm_mmap_data(pcm, data); } -int snd_pcm_munmap(snd_pcm_t *handle) +int snd_pcm_munmap(snd_pcm_t *pcm) { - int err; - err = snd_pcm_munmap_status(handle); - if (err < 0) - return err; - err = snd_pcm_munmap_control(handle); - if (err < 0) - return err; - return snd_pcm_munmap_data(handle); + return snd_pcm_munmap_data(pcm); } + +ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size) +{ + size_t xfer = 0; + ssize_t err = 0; + assert(size > 0); + while (xfer < size) { + size_t frames = size - xfer; + size_t offset = snd_pcm_mmap_hw_offset(pcm); + size_t cont = pcm->setup.buffer_size - offset; + if (cont < frames) + frames = cont; + if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + snd_pcm_channel_area_t *a = pcm->mmap_areas; + char *buf = snd_pcm_channel_area_addr(a, offset); + assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); + err = snd_pcm_writei(pcm, buf, frames); + } else { + size_t channels = pcm->setup.format.channels; + unsigned int c; + void *bufs[channels]; + assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); + for (c = 0; c < channels; ++c) { + snd_pcm_channel_area_t *a = &pcm->mmap_areas[c]; + bufs[c] = snd_pcm_channel_area_addr(a, offset); + } + err = snd_pcm_writen(pcm, bufs, frames); + } + if (err < 0) + break; + xfer += frames; + } + if (xfer > 0) + return xfer; + return err; +} + +ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size) +{ + size_t xfer = 0; + ssize_t err = 0; + assert(size > 0); + while (xfer < size) { + size_t frames = size - xfer; + size_t offset = snd_pcm_mmap_hw_offset(pcm); + size_t cont = pcm->setup.buffer_size - offset; + if (cont < frames) + frames = cont; + if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) { + snd_pcm_channel_area_t *a = pcm->mmap_areas; + char *buf = snd_pcm_channel_area_addr(a, offset); + assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED); + err = snd_pcm_readi(pcm, buf, frames); + } else { + size_t channels = pcm->setup.format.channels; + unsigned int c; + void *bufs[channels]; + assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED); + for (c = 0; c < channels; ++c) { + snd_pcm_channel_area_t *a = &pcm->mmap_areas[c]; + bufs[c] = snd_pcm_channel_area_addr(a, offset); + } + err = snd_pcm_readn(pcm, bufs, frames); + } + if (err < 0) + break; + xfer += frames; + } + if (xfer > 0) + return xfer; + return err; +} diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c new file mode 100644 index 00000000..14fc3c3a --- /dev/null +++ b/src/pcm/pcm_mulaw.c @@ -0,0 +1,536 @@ +/* + * PCM - Mu-Law conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef void (*mulaw_f)(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getputidx); + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int getput_idx; + mulaw_f func; + int sformat; + int cformat; + int cxfer_mode, cmmap_shape; +} snd_pcm_mulaw_t; + +static inline int val_seg(int val) +{ + int r = 0; + val >>= 7; + if (val & 0xf0) { + val >>= 4; + r += 4; + } + if (val & 0x0c) { + val >>= 2; + r += 2; + } + if (val & 0x02) + r += 1; + return r; +} + +/* + * s16_to_ulaw() - Convert a linear PCM value to u-law + * + * In order to simplify the encoding process, the original linear magnitude + * is biased by adding 33 which shifts the encoding range from (0 - 8158) to + * (33 - 8191). The result can be seen in the following encoding table: + * + * Biased Linear Input Code Compressed Code + * ------------------------ --------------- + * 00000001wxyza 000wxyz + * 0000001wxyzab 001wxyz + * 000001wxyzabc 010wxyz + * 00001wxyzabcd 011wxyz + * 0001wxyzabcde 100wxyz + * 001wxyzabcdef 101wxyz + * 01wxyzabcdefg 110wxyz + * 1wxyzabcdefgh 111wxyz + * + * Each biased linear code has a leading 1 which identifies the segment + * number. The value of the segment number is equal to 7 minus the number + * of leading 0's. The quantization interval is directly available as the + * four bits wxyz. * The trailing bits (a - h) are ignored. + * + * Ordinarily the complement of the resulting code word is used for + * transmission, and so the code word is complemented before it is returned. + * + * For further information see John C. Bellamy's Digital Telephony, 1982, + * John Wiley & Sons, pps 98-111 and 472-476. + */ + +static unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ +{ + int mask; + int seg; + unsigned char uval; + + if (pcm_val < 0) { + pcm_val = -pcm_val + 0x84; + mask = 0x7f; + } else { + pcm_val += 0x84; + mask = 0xff; + } + if (pcm_val > 0x7fff) + pcm_val = 0x7fff; + + /* Convert the scaled magnitude to segment number. */ + seg = val_seg(pcm_val); + + /* + * Combine the sign, segment, quantization bits; + * and complement the code word. + */ + uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); + return uval ^ mask; +} + +/* + * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM + * + * First, a biased linear code is derived from the code word. An unbiased + * output can then be obtained by subtracting 33 from the biased code. + * + * Note that this function expects to be passed the complement of the + * original code word. This is in keeping with ISDN conventions. + */ +static int ulaw_to_s16(unsigned char u_val) +{ + int t; + + /* Complement to obtain normal u-law value. */ + u_val = ~u_val; + + /* + * Extract and bias the quantization bits. Then + * shift up by the segment number and subtract out the bias. + */ + t = ((u_val & 0x0f) << 3) + 0x84; + t <<= (u_val & 0x70) >> 4; + + return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); +} + +static void mulaw_decode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int putidx) +{ +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef PUT_S16_LABELS + void *put = put_s16_labels[putidx]; + size_t channel; + for (channel = 0; channel < channels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + int16_t sample = ulaw_to_s16(*src); + goto *put; +#define PUT_S16_END after +#include "plugin_ops.h" +#undef PUT_S16_END + after: + src += src_step; + dst += dst_step; + } + } +} + +static void mulaw_encode(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, size_t channels, int getidx) +{ +#define GET_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS + void *get = get_s16_labels[getidx]; + size_t channel; + int16_t sample = 0; + for (channel = 0; channel < channels; ++channel) { + char *src; + char *dst; + int src_step, dst_step; + size_t frames1; + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + frames1 = frames; + while (frames1-- > 0) { + goto *get; +#define GET_S16_END after +#include "plugin_ops.h" +#undef GET_S16_END + after: + *dst = s16_to_ulaw(sample); + src += src_step; + dst += dst_step; + } + } +} + +static int snd_pcm_mulaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT) { + if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ? + !snd_pcm_format_linear(sfmt) : + sfmt != SND_PCM_SFMT_MU_LAW) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + } + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + info->req.format.sfmt = mulaw->sformat; + err = snd_pcm_params_info(mulaw->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = mulaw->sformat == SND_PCM_SFMT_MU_LAW ? + SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_MU_LAW; + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return err; +} + +static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + snd_pcm_t *slave = mulaw->plug.slave; + int err; + if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ? + !snd_pcm_format_linear(params->format.sfmt) : + params->format.sfmt != SND_PCM_SFMT_MU_LAW) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + mulaw->cformat = params->format.sfmt; + mulaw->cxfer_mode = params->xfer_mode; + mulaw->cmmap_shape = params->mmap_shape; + params->format.sfmt = mulaw->sformat; + params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; + params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; + err = snd_pcm_params(slave, params); + params->format.sfmt = mulaw->cformat; + params->xfer_mode = mulaw->cxfer_mode; + params->mmap_shape = mulaw->cmmap_shape; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_mulaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + int err = snd_pcm_setup(mulaw->plug.slave, setup); + if (err < 0) + return err; + assert(mulaw->sformat == setup->format.sfmt); + if (mulaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = mulaw->cxfer_mode; + if (mulaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = mulaw->cmmap_shape; + setup->format.sfmt = mulaw->cformat; + setup->mmap_bytes = 0; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) { + mulaw->getput_idx = getput_index(mulaw->cformat); + mulaw->func = mulaw_encode; + } else { + mulaw->getput_idx = getput_index(mulaw->sformat); + mulaw->func = mulaw_decode; + } + } else { + if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) { + mulaw->getput_idx = getput_index(mulaw->cformat); + mulaw->func = mulaw_decode; + } else { + mulaw->getput_idx = getput_index(mulaw->sformat); + mulaw->func = mulaw_encode; + } + } + return 0; +} + +static ssize_t snd_pcm_mulaw_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + snd_pcm_t *slave = mulaw->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + mulaw->func(areas, offset, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + frames, pcm->setup.format.channels, + mulaw->getput_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static ssize_t snd_pcm_mulaw_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + snd_pcm_t *slave = mulaw->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + mulaw->func(slave->mmap_areas, snd_pcm_mmap_offset(slave), + areas, offset, + frames, pcm->setup.format.channels, + mulaw->getput_idx); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_mulaw_t *mulaw = pcm->private; + fprintf(fp, "Mu-Law conversion PCM (%s)\n", + snd_pcm_format_name(mulaw->sformat)); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(mulaw->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_mulaw_ops = { + close: snd_pcm_plugin_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_mulaw_params_info, + params: snd_pcm_mulaw_params, + setup: snd_pcm_mulaw_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_plugin_channel_setup, + dump: snd_pcm_mulaw_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int snd_pcm_mulaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_mulaw_t *mulaw; + int err; + assert(handlep && slave); + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_MU_LAW) + return -EINVAL; + mulaw = calloc(1, sizeof(snd_pcm_mulaw_t)); + if (!mulaw) { + return -ENOMEM; + } + mulaw->sformat = sformat; + mulaw->plug.read = snd_pcm_mulaw_read_areas; + mulaw->plug.write = snd_pcm_mulaw_write_areas; + mulaw->plug.slave = slave; + mulaw->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(mulaw); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_MULAW; + handle->stream = slave->stream; + handle->ops = &snd_pcm_mulaw_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = mulaw; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1 && + sformat != SND_PCM_SFMT_MU_LAW) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname || !sformat) + return -EINVAL; + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_mulaw_open(pcmp, sformat, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 433e58f8..f697bd95 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -32,9 +32,6 @@ typedef struct { snd_pcm_t *handle; unsigned int channels_total; int close_slave; - char *buf; - snd_pcm_channel_area_t *areas; - struct iovec *iovec; } snd_pcm_multi_slave_t; typedef struct { @@ -49,9 +46,7 @@ typedef struct { size_t bindings_count; snd_pcm_multi_bind_t *bindings; size_t channels_count; - size_t frames_alloc; - int interleave; - int one_to_many; + int xfer_mode, mmap_shape; } snd_pcm_multi_t; static int snd_pcm_multi_close(snd_pcm_t *pcm) @@ -68,12 +63,6 @@ static int snd_pcm_multi_close(snd_pcm_t *pcm) ret = err; } else snd_pcm_unlink(slave->handle); - if (slave->buf) { - free(slave->buf); - free(slave->areas); - } - if (slave->iovec) - free(slave->iovec); } free(multi->slaves); free(multi->bindings); @@ -91,23 +80,12 @@ static int snd_pcm_multi_nonblock(snd_pcm_t *pcm, int nonblock) static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) { snd_pcm_multi_t *multi = pcm->private; - unsigned int i; int err; snd_pcm_t *handle_0 = multi->slaves[0].handle; + /* FIXME */ err = snd_pcm_info(handle_0, info); if (err < 0) return err; - for (i = 1; i < multi->slaves_count; ++i) { - snd_pcm_t *handle_i = multi->slaves[i].handle; - snd_pcm_info_t info_i; - memset(&info_i, 0, sizeof(info_i)); - err = snd_pcm_info(handle_i, &info_i); - if (err < 0) - return err; - info->flags &= info_i.flags; - } - if (multi->one_to_many) - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -118,7 +96,9 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info int err; snd_pcm_t *handle_0 = multi->slaves[0].handle; unsigned int old_mask = info->req_mask; - info->req_mask &= ~SND_PCM_PARAMS_CHANNELS; + info->req_mask &= ~(SND_PCM_PARAMS_CHANNELS | + SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); err = snd_pcm_params_info(handle_0, info); if (err < 0) return err; @@ -128,6 +108,8 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info snd_pcm_t *handle_i = multi->slaves[i].handle; snd_pcm_params_info_t info_i; info_i = *info; + info_i.req_mask |= SND_PCM_PARAMS_CHANNELS; + info_i.req.format.channels = multi->slaves[i].channels_total; err = snd_pcm_params_info(handle_i, &info_i); if (err < 0) return err; @@ -147,6 +129,7 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info info->min_fragments = info_i.min_fragments; if (info_i.max_fragments < info->max_fragments) info->max_fragments = info_i.max_fragments; + info->flags &= info_i.flags; } info->req_mask = old_mask; return 0; @@ -159,36 +142,39 @@ static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params) snd_pcm_params_t p; if (params->format.channels != multi->channels_count) return -EINVAL; + multi->xfer_mode = params->xfer_mode; + multi->mmap_shape = params->mmap_shape; p = *params; - multi->interleave = params->format.interleave; for (i = 0; i < multi->slaves_count; ++i) { int err; snd_pcm_t *handle = multi->slaves[i].handle; - snd_pcm_info_t info; - err = snd_pcm_info(handle, &info); - if (err < 0) - return err; - p.format.interleave = params->format.interleave; - if (!(info.flags & SND_PCM_INFO_INTERLEAVE)) - p.format.interleave = 0; - else if (!(info.flags & SND_PCM_INFO_NONINTERLEAVE)) - p.format.interleave = 1; + if (handle->mmap_data) { + err = snd_pcm_munmap_data(handle); + if (err < 0) + return err; + } + p.xfer_mode = SND_PCM_XFER_UNSPECIFIED; + p.mmap_shape = SND_PCM_MMAP_UNSPECIFIED; p.format.channels = multi->slaves[i].channels_total; #if 1 p.xrun_max = ~0; #endif err = snd_pcm_params(handle, &p); - if (err < 0) + if (err < 0) { + params->fail_mask = p.fail_mask; + params->fail_reason = p.fail_reason; return err; - if (i == 0 && params->mode == SND_PCM_MODE_FRAGMENT) { - snd_pcm_setup_t s; - err = snd_pcm_setup(handle, &s); - if (err < 0) - return err; - p.frag_size = s.frag_size; - p.buffer_size = s.buffer_size; } } + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_mmap_data(handle, NULL); + if (err < 0) + return err; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + snd_pcm_areas_silence(handle->mmap_areas, 0, handle->setup.format.channels, + handle->setup.buffer_size, handle->setup.format.sfmt); + } return 0; } @@ -202,7 +188,6 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) if (err < 0) return err; frames_alloc = multi->slaves[0].handle->setup.frag_size; - multi->frames_alloc = 0; for (i = 1; i < multi->slaves_count; ++i) { snd_pcm_setup_t s; snd_pcm_t *sh = multi->slaves[i].handle; @@ -211,53 +196,21 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) return err; if (setup->format.rate != s.format.rate) return -EINVAL; - if (setup->align % s.align != 0) + /* mmap is not feasible */ + if (setup->buffer_size != s.buffer_size) return -EINVAL; + if (setup->mmap_shape != SND_PCM_MMAP_NONINTERLEAVED || + s.mmap_shape != SND_PCM_MMAP_NONINTERLEAVED) + setup->mmap_shape = SND_PCM_MMAP_COMPLEX; } - setup->format.interleave = multi->interleave; setup->format.channels = multi->channels_count; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_multi_slave_t *s = &multi->slaves[i]; - snd_pcm_t *sh = s->handle; - unsigned int c; - if (s->buf) { - free(s->buf); - s->buf = 0; - free(s->areas); - s->areas = 0; - } - if (s->iovec) - free(s->iovec); - if (!sh->setup.format.interleave) { - s->iovec = calloc(s->channels_total, sizeof(*s->iovec)); - if (!pcm->setup.format.interleave) - continue; - } - s->buf = malloc(frames_alloc * sh->bits_per_frame / 8); - if (!s->buf) - return -ENOMEM; - snd_pcm_format_set_silence(sh->setup.format.format, s->buf, - sh->setup.frag_size * sh->setup.format.channels); - s->areas = calloc(s->channels_total, sizeof(*s->areas)); - if (!s->areas) - return -ENOMEM; - for (c = 0; c < s->channels_total; ++c) { - snd_pcm_channel_area_t *a = &s->areas[c]; - if (sh->setup.format.interleave) { - a->addr = s->buf; - a->first = c * sh->bits_per_sample; - a->step = sh->bits_per_frame; - } else { - a->addr = s->buf + sh->setup.frag_size * sh->bits_per_sample / 8; - a->first = 0; - a->step = sh->bits_per_sample; - s->iovec[c].iov_base = a->addr; - } - } - } - multi->frames_alloc = frames_alloc; - /* Loaded with a value != 0 if mmap is feasible */ - setup->mmap_bytes = !multi->one_to_many; + if (multi->xfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = multi->xfer_mode; + if (multi->mmap_shape != SND_PCM_MMAP_UNSPECIFIED && + multi->mmap_shape != setup->mmap_shape) + return -EINVAL; return 0; } @@ -275,11 +228,18 @@ static int snd_pcm_multi_state(snd_pcm_t *pcm) return snd_pcm_state(handle); } -static ssize_t snd_pcm_multi_hw_ptr(snd_pcm_t *pcm, int update) +static int snd_pcm_multi_delay(snd_pcm_t *pcm, ssize_t *delayp) { snd_pcm_multi_t *multi = pcm->private; snd_pcm_t *handle = multi->slaves[0].handle; - return snd_pcm_hw_ptr(handle, update); + return snd_pcm_delay(handle, delayp); +} + +static ssize_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_multi_t *multi = pcm->private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_avail_update(handle); } static int snd_pcm_multi_prepare(snd_pcm_t *pcm) @@ -288,16 +248,16 @@ static int snd_pcm_multi_prepare(snd_pcm_t *pcm) return snd_pcm_prepare(multi->slaves[0].handle); } -static int snd_pcm_multi_go(snd_pcm_t *pcm) +static int snd_pcm_multi_start(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; - return snd_pcm_go(multi->slaves[0].handle); + return snd_pcm_start(multi->slaves[0].handle); } -static int snd_pcm_multi_drain(snd_pcm_t *pcm) +static int snd_pcm_multi_stop(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; - return snd_pcm_drain(multi->slaves[0].handle); + return snd_pcm_stop(multi->slaves[0].handle); } static int snd_pcm_multi_flush(snd_pcm_t *pcm) @@ -394,206 +354,21 @@ static ssize_t snd_pcm_multi_appl_ptr(snd_pcm_t *pcm, off_t offset) return newpos; } -static int snd_pcm_multi_write_copy(snd_pcm_t *pcm, const void *buf, - size_t offset, size_t count) -{ - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - snd_pcm_channel_area_t area; - area.addr = (void *) buf + offset * pcm->bits_per_frame; - area.step = pcm->bits_per_frame; - for (i = 0; i < multi->bindings_count; ++i) { - snd_pcm_multi_bind_t *bind = &multi->bindings[i]; - snd_pcm_multi_slave_t *slave = &multi->slaves[bind->slave]; - int err; - assert(slave->buf); - area.first = pcm->bits_per_sample * bind->client_channel; - err = snd_pcm_area_copy(&area, 0, &slave->areas[bind->slave_channel], 0, count, pcm->setup.format.format); - if (err < 0) - return err; - if (!slave->handle->setup.format.interleave) { - struct iovec *vec = &slave->iovec[bind->slave_channel]; - vec->iov_len = count; - } - } - return 0; -} - -static int snd_pcm_multi_writev_copy(snd_pcm_t *pcm, const struct iovec *vec, - size_t offset, size_t count) -{ - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - snd_pcm_channel_area_t area; - area.first = 0; - area.step = pcm->bits_per_sample; - for (i = 0; i < multi->bindings_count; ++i) { - snd_pcm_multi_bind_t *bind = &multi->bindings[i]; - snd_pcm_multi_slave_t *slave = &multi->slaves[bind->slave]; - int err; - area.addr = vec[bind->client_channel].iov_base + - offset * pcm->bits_per_sample; - if (slave->handle->setup.format.interleave) { - assert(slave->buf); - err = snd_pcm_area_copy(&area, 0, &slave->areas[bind->slave_channel], 0, count, pcm->setup.format.format); - if (err < 0) - return err; - } else { - struct iovec *vec = &slave->iovec[bind->slave_channel]; - vec->iov_base = area.addr; - vec->iov_len = count; - } - } - return 0; -} - -static ssize_t snd_pcm_multi_write_io(snd_pcm_t *pcm, size_t count) -{ - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - ssize_t frames = count; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_multi_slave_t *slave = &multi->slaves[i]; - snd_pcm_t *sh = slave->handle; - if (sh->setup.format.interleave) { - frames = snd_pcm_write(sh, slave->buf, frames); - } else { - int channels = sh->setup.format.channels; - frames = snd_pcm_writev(sh, slave->iovec, channels); - } - if (frames <= 0) - break; - } - return frames; -} - -static ssize_t snd_pcm_multi_write(snd_pcm_t *pcm, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const void *buf, size_t count) -{ - snd_pcm_multi_t *multi = pcm->private; - size_t result = 0; - while (count > 0) { - int err; - ssize_t ret; - size_t frames = count; - if (frames > multi->frames_alloc) - frames = multi->frames_alloc; - err = snd_pcm_multi_write_copy(pcm, buf, result, frames); - if (err < 0) - return err; - ret = snd_pcm_multi_write_io(pcm, frames); - if (ret > 0) - result += ret; - if (ret != (ssize_t)frames) { - if (result > 0) - return result; - return ret; - } - count -= ret; - } - return result; -} - -static ssize_t snd_pcm_multi_writev1(snd_pcm_t *pcm, const struct iovec *vector, size_t count) -{ - snd_pcm_multi_t *multi = pcm->private; - size_t result = 0; - while (count > 0) { - int err; - ssize_t ret; - size_t frames = count; - if (frames > multi->frames_alloc) - frames = multi->frames_alloc; - err = snd_pcm_multi_writev_copy(pcm, vector, result, frames); - if (err < 0) - return err; - ret = snd_pcm_multi_write_io(pcm, frames); - if (ret > 0) - result += ret; - if (ret != (ssize_t) frames) { - if (result > 0) - return result; - return ret; - } - count -= ret; - } - return result; -} - -static ssize_t snd_pcm_multi_writev(snd_pcm_t *pcm, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const struct iovec *vector, unsigned long count) -{ - unsigned int k, step; - size_t result = 0; - if (pcm->setup.format.interleave) - step = 1; - else - step = pcm->setup.format.channels; - for (k = 0; k < count; k += step) { - ssize_t ret; - if (pcm->setup.format.interleave) - ret = snd_pcm_multi_write(pcm, timestamp, vector->iov_base, vector->iov_len); - else - ret = snd_pcm_multi_writev1(pcm, vector, vector->iov_len); - if (ret > 0) - result += ret; - if (ret != (ssize_t) vector->iov_len) { - if (result > 0) - return result; - return ret; - } - vector += step; - } - return result; -} - -static ssize_t snd_pcm_multi_read(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, void *buf ATTRIBUTE_UNUSED, size_t count ATTRIBUTE_UNUSED) -{ - // snd_pcm_multi_t *multi = pcm->private; - return -ENOSYS; -} - -static ssize_t snd_pcm_multi_readv(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_timestamp_t *timestamp ATTRIBUTE_UNUSED, const struct iovec *vector ATTRIBUTE_UNUSED, unsigned long count ATTRIBUTE_UNUSED) -{ - // snd_pcm_multi_t *multi = pcm->private; - return -ENOSYS; -} - -static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t **status) +static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *handle = multi->slaves[i].handle; - int err = snd_pcm_mmap_status(handle, status); - if (err < 0) - return err; - } - *status = multi->slaves[0].handle->mmap_status; + pcm->mmap_status = multi->slaves[0].handle->mmap_status; return 0; } -static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t **control) +static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; - snd_pcm_setup_t *setup_0 = &multi->slaves[0].handle->setup; - unsigned int i; - for (i = 1; i < multi->slaves_count; ++i) { - snd_pcm_setup_t *setup = &multi->slaves[i].handle->setup; - /* Don't permit mmap if appl_ptr's have - different ranges */ - if (setup->buffer_size != setup_0->buffer_size) - return -EBADFD; - } - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *handle = multi->slaves[i].handle; - int err = snd_pcm_mmap_control(handle, control); - if (err < 0) - return err; - } - *control = multi->slaves[0].handle->mmap_control; + pcm->mmap_control = multi->slaves[0].handle->mmap_control; return 0; } -static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize ATTRIBUTE_UNUSED) +static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; @@ -609,95 +384,97 @@ static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm, void **buffer, size_t bsize A err = snd_pcm_mmap_get_areas(handle, areas); if (err < 0) return err; - err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.format); + err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt); if (err < 0) return err; } } - *buffer = multi->slaves[0].handle->mmap_data; + pcm->mmap_data = multi->slaves[0].handle->mmap_data; return 0; } -static int snd_pcm_multi_munmap_status(snd_pcm_t *pcm, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED) +static int snd_pcm_multi_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED) { - snd_pcm_multi_t *multi = pcm->private; - unsigned int i; - int ret = 0; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *handle = multi->slaves[i].handle; - int err = snd_pcm_munmap_status(handle); - if (err < 0) - ret = err; - } - return ret; + return 0; +} + +static int snd_pcm_multi_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; } -static int snd_pcm_multi_munmap_control(snd_pcm_t *pcm, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED) +static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; int ret = 0; for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *handle = multi->slaves[i].handle; - int err = snd_pcm_munmap_control(handle); + int err = snd_pcm_munmap_data(handle); if (err < 0) ret = err; } return ret; } -static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED, size_t size ATTRIBUTE_UNUSED) +static ssize_t snd_pcm_multi_mmap_forward(snd_pcm_t *pcm, size_t size) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; - int ret = 0; + for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *handle = multi->slaves[i].handle; - int err = snd_pcm_munmap_data(handle); - if (err < 0) - ret = err; + ssize_t frames = snd_pcm_mmap_forward(handle, size); + if (frames < 0) + return frames; + if (i == 0) { + size = frames; + continue; + } + if ((size_t) frames != size) + return -EBADFD; } - return ret; + return size; } - -static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *client_vmask) + +static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *cmask) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; - bitset_t *vmasks[multi->slaves_count]; + bitset_t *cmasks[multi->slaves_count]; int err; for (i = 0; i < multi->slaves_count; ++i) - vmasks[i] = bitset_alloc(multi->slaves[i].channels_total); + cmasks[i] = bitset_alloc(multi->slaves[i].channels_total); for (i = 0; i < multi->bindings_count; ++i) { snd_pcm_multi_bind_t *b = &multi->bindings[i]; - if (bitset_get(client_vmask, b->client_channel)) - bitset_set(vmasks[b->slave], b->slave_channel); + if (bitset_get(cmask, b->client_channel)) + bitset_set(cmasks[b->slave], b->slave_channel); } for (i = 0; i < multi->slaves_count; ++i) { snd_pcm_t *handle = multi->slaves[i].handle; - err = snd_pcm_channels_mask(handle, vmasks[i]); + err = snd_pcm_channels_mask(handle, cmasks[i]); if (err < 0) { for (i = 0; i <= multi->slaves_count; ++i) - free(vmasks[i]); + free(cmasks[i]); return err; } } - bitset_zero(client_vmask, pcm->setup.format.channels); + bitset_zero(cmask, pcm->setup.format.channels); for (i = 0; i < multi->bindings_count; ++i) { snd_pcm_multi_bind_t *b = &multi->bindings[i]; - if (bitset_get(vmasks[b->slave], b->slave_channel)) - bitset_set(client_vmask, b->client_channel); + if (bitset_get(cmasks[b->slave], b->slave_channel)) + bitset_set(cmask, b->client_channel); } for (i = 0; i < multi->slaves_count; ++i) - free(vmasks[i]); + free(cmasks[i]); return 0; } -int snd_pcm_multi_file_descriptor(snd_pcm_t *pcm) +int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm) { snd_pcm_multi_t *multi = pcm->private; snd_pcm_t *handle = multi->slaves[0].handle; - return snd_pcm_file_descriptor(handle); + return snd_pcm_poll_descriptor(handle); } static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp) @@ -728,35 +505,37 @@ struct snd_pcm_ops snd_pcm_multi_ops = { params_info: snd_pcm_multi_params_info, params: snd_pcm_multi_params, setup: snd_pcm_multi_setup, + channel_info: snd_pcm_multi_channel_info, + channel_params: snd_pcm_multi_channel_params, + channel_setup: snd_pcm_multi_channel_setup, dump: snd_pcm_multi_dump, + nonblock: snd_pcm_multi_nonblock, + mmap_status: snd_pcm_multi_mmap_status, + mmap_control: snd_pcm_multi_mmap_control, + mmap_data: snd_pcm_multi_mmap_data, + munmap_status: snd_pcm_multi_munmap_status, + munmap_control: snd_pcm_multi_munmap_control, + munmap_data: snd_pcm_multi_munmap_data, }; struct snd_pcm_fast_ops snd_pcm_multi_fast_ops = { - nonblock: snd_pcm_multi_nonblock, - channel_info: snd_pcm_multi_channel_info, - channel_params: snd_pcm_multi_channel_params, - channel_setup: snd_pcm_multi_channel_setup, status: snd_pcm_multi_status, - hw_ptr: snd_pcm_multi_hw_ptr, state: snd_pcm_multi_state, + delay: snd_pcm_multi_delay, prepare: snd_pcm_multi_prepare, - go: snd_pcm_multi_go, - drain: snd_pcm_multi_drain, + start: snd_pcm_multi_start, + stop: snd_pcm_multi_stop, flush: snd_pcm_multi_flush, pause: snd_pcm_multi_pause, - write: snd_pcm_multi_write, - writev: snd_pcm_multi_writev, - read: snd_pcm_multi_read, - readv: snd_pcm_multi_readv, + writei: snd_pcm_mmap_writei, + writen: snd_pcm_mmap_writen, + readi: snd_pcm_mmap_readi, + readn: snd_pcm_mmap_readn, appl_ptr: snd_pcm_multi_appl_ptr, - mmap_status: snd_pcm_multi_mmap_status, - mmap_control: snd_pcm_multi_mmap_control, - mmap_data: snd_pcm_multi_mmap_data, - munmap_status: snd_pcm_multi_munmap_status, - munmap_control: snd_pcm_multi_munmap_control, - munmap_data: snd_pcm_multi_munmap_data, - file_descriptor: snd_pcm_multi_file_descriptor, + poll_descriptor: snd_pcm_multi_poll_descriptor, channels_mask: snd_pcm_multi_channels_mask, + avail_update: snd_pcm_multi_avail_update, + mmap_forward: snd_pcm_multi_mmap_forward, }; int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, @@ -769,6 +548,7 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, snd_pcm_multi_t *multi; size_t channels = 0; unsigned int i; + int err; int stream; char client_map[32] = { 0 }; char slave_map[32][32] = { { 0 } }; @@ -777,12 +557,8 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, assert(slaves_count > 0 && slaves_handle && schannels_count); assert(bindings_count > 0 && bindings_slave && bindings_cchannel && bindings_schannel); - handle = calloc(1, sizeof(snd_pcm_t)); - if (!handle) - return -ENOMEM; multi = calloc(1, sizeof(snd_pcm_multi_t)); if (!multi) { - free(handle); return -ENOMEM; } @@ -808,21 +584,20 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, bind->client_channel = bindings_cchannel[i]; bind->slave = bindings_slave[i]; bind->slave_channel = bindings_schannel[i]; - if (slave_map[bindings_slave[i]][bindings_schannel[i]]) { - assert(stream == SND_PCM_STREAM_CAPTURE); - multi->one_to_many = 1; - } + assert(!slave_map[bindings_slave[i]][bindings_schannel[i]]); slave_map[bindings_slave[i]][bindings_schannel[i]] = 1; - if (client_map[bindings_cchannel[i]]) { - assert(stream == SND_PCM_STREAM_PLAYBACK); - multi->one_to_many = 1; - } + assert(!client_map[bindings_cchannel[i]]); client_map[bindings_cchannel[i]] = 1; if (bindings_cchannel[i] >= channels) channels = bindings_cchannel[i] + 1; } multi->channels_count = channels; + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(multi); + return -ENOMEM; + } handle->type = SND_PCM_TYPE_MULTI; handle->stream = stream; handle->mode = multi->slaves[0].handle->mode; @@ -831,6 +606,195 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, handle->fast_ops = &snd_pcm_multi_fast_ops; handle->fast_op_arg = handle; handle->private = multi; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } *handlep = handle; return 0; } + +int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i, j; + snd_config_t *slave = NULL; + snd_config_t *binding = NULL; + int err; + unsigned int idx; + char **slaves_id = NULL; + char **slaves_name = NULL; + snd_pcm_t **slaves_pcm = NULL; + size_t *slaves_channels = NULL; + unsigned int *bindings_cchannel = NULL; + unsigned int *bindings_slave = NULL; + unsigned int *bindings_schannel = NULL; + size_t slaves_count = 0; + size_t bindings_count = 0; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "slave") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + slave = n; + continue; + } + if (strcmp(n->id, "binding") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + binding = n; + continue; + } + return -EINVAL; + } + if (!slave || !binding) + return -EINVAL; + snd_config_foreach(i, slave) { + ++slaves_count; + } + snd_config_foreach(i, binding) { + ++bindings_count; + } + slaves_id = calloc(slaves_count, sizeof(*slaves_id)); + slaves_name = calloc(slaves_count, sizeof(*slaves_name)); + slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm)); + slaves_channels = calloc(slaves_count, sizeof(*slaves_channels)); + bindings_cchannel = calloc(bindings_count, sizeof(*bindings_cchannel)); + bindings_slave = calloc(bindings_count, sizeof(*bindings_slave)); + bindings_schannel = calloc(bindings_count, sizeof(*bindings_schannel)); + idx = 0; + snd_config_foreach(i, slave) { + snd_config_t *m = snd_config_entry(i); + char *pcm = NULL; + long channels = -1; + slaves_id[idx] = snd_config_id(m); + snd_config_foreach(j, m) { + snd_config_t *n = snd_config_entry(j); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "pcm") == 0) { + err = snd_config_string_get(n, &pcm); + if (err < 0) + goto _free; + continue; + } + if (strcmp(n->id, "channels") == 0) { + err = snd_config_integer_get(n, &channels); + if (err < 0) + goto _free; + continue; + } + err = -EINVAL; + goto _free; + } + if (!pcm || channels < 0) { + err = -EINVAL; + goto _free; + } + slaves_name[idx] = strdup(pcm); + slaves_channels[idx] = channels; + ++idx; + } + + idx = 0; + snd_config_foreach(i, binding) { + snd_config_t *m = snd_config_entry(i); + long cchannel = -1, schannel = -1; + int slave = -1; + long val; + char *str; + snd_config_foreach(j, m) { + snd_config_t *n = snd_config_entry(j); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "client_channel") == 0) { + err = snd_config_integer_get(n, &cchannel); + if (err < 0) + goto _free; + continue; + } + if (strcmp(n->id, "slave") == 0) { + char buf[32]; + unsigned int k; + err = snd_config_string_get(n, &str); + if (err < 0) { + err = snd_config_integer_get(n, &val); + if (err < 0) + goto _free; + sprintf(buf, "%ld", val); + str = buf; + } + for (k = 0; k < slaves_count; ++k) { + if (strcmp(slaves_id[k], str) == 0) + slave = k; + } + continue; + } + if (strcmp(n->id, "slave_channel") == 0) { + err = snd_config_integer_get(n, &schannel); + if (err < 0) + goto _free; + continue; + } + err = -EINVAL; + goto _free; + } + if (cchannel < 0 || slave < 0 || schannel < 0) { + err = -EINVAL; + goto _free; + } + if ((size_t)slave >= slaves_count) { + err = -EINVAL; + goto _free; + } + if ((unsigned int) schannel >= slaves_channels[slave]) { + err = -EINVAL; + goto _free; + } + bindings_cchannel[idx] = cchannel; + bindings_slave[idx] = slave; + bindings_schannel[idx] = schannel; + ++idx; + } + + for (idx = 0; idx < slaves_count; ++idx) { + err = snd_pcm_open(&slaves_pcm[idx], slaves_name[idx], stream, mode); + if (err < 0) + goto _free; + } + err = snd_pcm_multi_create(pcmp, slaves_count, slaves_pcm, + slaves_channels, + bindings_count, bindings_cchannel, + bindings_slave, bindings_schannel, + 1); +_free: + if (err < 0) { + for (idx = 0; idx < slaves_count; ++idx) { + if (slaves_pcm[idx]) + snd_pcm_close(slaves_pcm[idx]); + if (slaves_name[idx]) + free(slaves_name[idx]); + } + } + if (slaves_name) + free(slaves_name); + if (slaves_pcm) + free(slaves_pcm); + if (slaves_channels) + free(slaves_channels); + if (bindings_cchannel) + free(bindings_cchannel); + if (bindings_slave) + free(bindings_slave); + if (bindings_schannel) + free(bindings_schannel); + return err; +} + diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index adb41aae..adf491fa 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -19,125 +19,168 @@ * */ -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <math.h> -#include <sys/uio.h> -#include <limits.h> #include "pcm_local.h" +#include "pcm_plugin.h" -/* snd_pcm_plugin externs */ -int snd_pcm_plugin_insert(snd_pcm_plugin_t *plugin) -{ - snd_pcm_plug_t *plug; - assert(plugin); - plug = plugin->plug; - plugin->next = plug->first; - plugin->prev = NULL; - if (plug->first) { - plug->first->prev = plugin; - plug->first = plugin; - } else { - plug->last = - plug->first = plugin; - } - return 0; -} +typedef struct { + snd_pcm_t *req_slave; + int close_slave; + snd_pcm_t *slave; + ttable_entry_t *ttable; + unsigned int tt_ssize, tt_cused, tt_sused; +} snd_pcm_plug_t; + + +unsigned int snd_pcm_plug_formats(unsigned int formats) +{ + int fmts = (SND_PCM_LINEAR_FORMATS | SND_PCM_FMT_MU_LAW | + SND_PCM_FMT_A_LAW | SND_PCM_FMT_IMA_ADPCM); + if (formats & fmts) + formats |= fmts; + return formats; +} + +static int preferred_formats[] = { + SND_PCM_SFMT_S16_LE, + SND_PCM_SFMT_S16_BE, + SND_PCM_SFMT_U16_LE, + SND_PCM_SFMT_U16_BE, + SND_PCM_SFMT_S24_LE, + SND_PCM_SFMT_S24_BE, + SND_PCM_SFMT_U24_LE, + SND_PCM_SFMT_U24_BE, + SND_PCM_SFMT_S32_LE, + SND_PCM_SFMT_S32_BE, + SND_PCM_SFMT_U32_LE, + SND_PCM_SFMT_U32_BE, + SND_PCM_SFMT_S8, + SND_PCM_SFMT_U8 +}; -int snd_pcm_plugin_append(snd_pcm_plugin_t *plugin) +static int snd_pcm_plug_slave_fmt(int format, + snd_pcm_params_info_t *slave_info) { - snd_pcm_plug_t *plug; - assert(plugin); - plug = plugin->plug; - plugin->next = NULL; - plugin->prev = plug->last; - if (plug->last) { - plug->last->next = plugin; - plug->last = plugin; + 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); + int unsignd = snd_pcm_format_unsigned(format); + int big = snd_pcm_format_big_endian(format); + int format1; + int wid, width1=width; + int dwidth1 = 8; + for (wid = 0; wid < 4; ++wid) { + int end, big1 = big; + for (end = 0; end < 2; ++end) { + int sgn, unsignd1 = unsignd; + for (sgn = 0; sgn < 2; ++sgn) { + format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); + if (format1 >= 0 && + slave_info->formats & (1 << format1)) + goto _found; + unsignd1 = !unsignd1; + } + big1 = !big1; + } + if (width1 == 32) { + dwidth1 = -dwidth1; + width1 = width; + } + width1 += dwidth1; + } + return -EINVAL; + _found: + return format1; } else { - plug->last = - plug->first = plugin; - } - return 0; -} - -void snd_pcm_plugin_dump(snd_pcm_plugin_t *plugin, FILE *fp) -{ - fprintf(fp, "----------- %s\n", plugin->name); - fprintf(fp, "Buffer: %ld frames\n", (long)plugin->buf_frames); - if (plugin->src_format.interleave != plugin->dst_format.interleave) { - if (plugin->src_format.interleave) - fprintf(fp, "Interleaved -> Non interleaved\n"); - else - fprintf(fp, "Non interleaved -> Interleaved\n"); - } - if (plugin->src_format.channels != plugin->dst_format.channels) { - fprintf(fp, "Channels: %d -> %d\n", - plugin->src_format.channels, - plugin->dst_format.channels); - } - if (plugin->src_format.format != plugin->dst_format.format) { - fprintf(fp, "Format: %s -> %s\n", - snd_pcm_format_name(plugin->src_format.format), - snd_pcm_format_name(plugin->dst_format.format)); - } - if (plugin->src_format.rate != plugin->dst_format.rate) { - fprintf(fp, "Rate: %d -> %d\n", - plugin->src_format.rate, - plugin->dst_format.rate); + unsigned int i; + switch (format) { + case SND_PCM_SFMT_MU_LAW: + case SND_PCM_SFMT_A_LAW: + case SND_PCM_SFMT_IMA_ADPCM: + for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { + int format1 = preferred_formats[i]; + if (slave_info->formats & (1 << format1)) + return format1; + } + default: + return -EINVAL; + } } - if (plugin->dump) - plugin->dump(plugin, fp); } -/* snd_pcm_plug externs */ +struct { + unsigned int rate; + unsigned int flag; +} snd_pcm_rates[] = { + { 8000, SND_PCM_RATE_8000 }, + { 11025, SND_PCM_RATE_11025 }, + { 16000, SND_PCM_RATE_16000 }, + { 22050, SND_PCM_RATE_22050 }, + { 32000, SND_PCM_RATE_32000 }, + { 44100, SND_PCM_RATE_44100 }, + { 48000, SND_PCM_RATE_48000 }, + { 88200, SND_PCM_RATE_88200 }, + { 96000, SND_PCM_RATE_96000 }, + { 176400, SND_PCM_RATE_176400 }, + { 192000, SND_PCM_RATE_192000 } +}; -int snd_pcm_plug_clear(snd_pcm_plug_t *plug) -{ - snd_pcm_plugin_t *plugin, *plugin_next; - - assert(plug); - - plugin = plug->first; - plug->first = NULL; - plug->last = NULL; - while (plugin) { - plugin_next = plugin->next; - snd_pcm_plugin_free(plugin); - plugin = plugin_next; +static int snd_pcm_plug_slave_rate(unsigned int rate, + snd_pcm_params_info_t *slave_info) +{ + if (rate <= slave_info->min_rate) + return slave_info->min_rate; + else if (rate >= slave_info->max_rate) + return slave_info->max_rate; + else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS | + SND_PCM_RATE_KNOT))) { + unsigned int k; + unsigned int rate1 = 0, rate2 = 0; + int delta1, delta2; + for (k = 0; k < sizeof(snd_pcm_rates) / + sizeof(snd_pcm_rates[0]); ++k) { + if (!(snd_pcm_rates[k].flag & slave_info->rates)) + continue; + if (snd_pcm_rates[k].rate < rate) { + rate1 = snd_pcm_rates[k].rate; + } else if (snd_pcm_rates[k].rate >= rate) { + rate2 = snd_pcm_rates[k].rate; + break; + } + } + if (rate1 == 0) + return rate2; + if (rate2 == 0) + return rate1; + delta1 = rate - rate1; + delta2 = rate2 - rate; + if (delta1 < delta2) + return rate1; + else + return rate2; } - return 0; + return rate; } -snd_pcm_plugin_t *snd_pcm_plug_first(snd_pcm_plug_t *plug) -{ - assert(plug); - return plug->first; -} - -snd_pcm_plugin_t *snd_pcm_plug_last(snd_pcm_plug_t *plug) -{ - assert(plug); - return plug->last; -} - -/* - * - */ - static int snd_pcm_plug_close(snd_pcm_t *pcm) { snd_pcm_plug_t *plug = pcm->private; - snd_pcm_plug_clear(plug); - free(plug->handle->fast_ops); - if (plug->close_slave) - return snd_pcm_close(plug->slave); + int err, result = 0; + if (plug->ttable) + free(plug->ttable); + if (plug->slave != plug->req_slave) { + err = snd_pcm_close(plug->slave); + if (err < 0) + result = err; + } + if (plug->close_slave) { + err = snd_pcm_close(plug->req_slave); + if (err < 0) + result = err; + } free(plug); - return 0; + return result; } static int snd_pcm_plug_nonblock(snd_pcm_t *pcm, int nonblock) @@ -148,13 +191,12 @@ static int snd_pcm_plug_nonblock(snd_pcm_t *pcm, int nonblock) static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info) { - int err; snd_pcm_plug_t *plug = pcm->private; + snd_pcm_t *slave = plug->req_slave; + int err; - if ((err = snd_pcm_info(plug->slave, info)) < 0) + if ((err = snd_pcm_info(slave, info)) < 0) return err; - info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - info->flags |= SND_PCM_INFO_INTERLEAVE | SND_PCM_INFO_NONINTERLEAVE; return 0; } @@ -162,12 +204,12 @@ static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) { int err; snd_pcm_plug_t *plug = pcm->private; + snd_pcm_t *slave = plug->req_slave; snd_pcm_params_info_t slave_info; - int rate; - int slave_format, slave_rate; - unsigned int slave_channels; + int sformat, srate; + unsigned int schannels; + int crate; - memset(&info->formats, 0, (char*)(info + 1) - (char*) &info->formats); info->req.fail_reason = 0; info->req.fail_mask = 0; @@ -178,8 +220,8 @@ static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) info->min_rate = 4000; info->max_rate = 192000; } - /* ??? */ info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000; + if (info->req_mask & SND_PCM_PARAMS_CHANNELS) { info->min_channels = info->req.format.channels; info->max_channels = info->req.format.channels; @@ -189,30 +231,34 @@ static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) } memset(&slave_info, 0, sizeof(slave_info)); - if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0) + if ((err = snd_pcm_params_info(slave, &slave_info)) < 0) return err; - if (info->req_mask & SND_PCM_PARAMS_FORMAT) - info->formats = 1 << info->req.format.format; - else - info->formats = snd_pcm_plug_formats(slave_info.formats); + info->flags = slave_info.flags; + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; info->min_fragments = slave_info.min_fragments; info->max_fragments = slave_info.max_fragments; - if (!(info->req_mask & SND_PCM_PARAMS_FORMAT)) + if (info->req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << info->req.format.sfmt; + else { + info->formats = snd_pcm_plug_formats(slave_info.formats); return 0; - slave_format = snd_pcm_plug_slave_fmt(info->req.format.format, &slave_info); - if (slave_format < 0) { - info->req.fail_mask = SND_PCM_PARAMS_FORMAT; + } + + sformat = snd_pcm_plug_slave_fmt(info->req.format.sfmt, &slave_info); + if (sformat < 0) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; return -EINVAL; } if (!(info->req_mask & SND_PCM_PARAMS_RATE)) return 0; - slave_rate = snd_pcm_plug_slave_rate(info->req.format.rate, &slave_info); - if (slave_rate < 0) { + crate = info->req.format.rate; + srate = snd_pcm_plug_slave_rate(crate, &slave_info); + if (srate < 0) { info->req.fail_mask = SND_PCM_PARAMS_RATE; info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; return -EINVAL; @@ -220,619 +266,418 @@ static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info) if (!(info->req_mask & SND_PCM_PARAMS_CHANNELS)) return 0; - slave_channels = info->req.format.rate; - if (slave_channels < info->min_channels) - slave_channels = info->min_channels; - else if (slave_channels > info->max_channels) - slave_channels = info->max_channels; + schannels = info->req.format.channels; + if (schannels < info->min_channels) + schannels = info->min_channels; + else if (schannels > info->max_channels) + schannels = info->max_channels; - slave_info.req_mask = (SND_PCM_PARAMS_FORMAT | + slave_info.req_mask = (SND_PCM_PARAMS_SFMT | SND_PCM_PARAMS_CHANNELS | SND_PCM_PARAMS_RATE); - slave_info.req.format.format = info->req.format.format; - slave_info.req.format.channels = info->req.format.channels; - slave_info.req.format.rate = info->req.format.rate; - if ((err = snd_pcm_params_info(plug->slave, &slave_info)) < 0) { + slave_info.req.format.sfmt = sformat; + slave_info.req.format.channels = schannels; + slave_info.req.format.rate = srate; + if ((err = snd_pcm_params_info(slave, &slave_info)) < 0) { info->req.fail_mask = slave_info.req.fail_mask; info->req.fail_reason = slave_info.req.fail_reason; return err; } - rate = info->req.format.rate; - info->buffer_size = slave_info.buffer_size * rate / slave_rate; - info->min_fragment_size = slave_info.min_fragment_size * rate / slave_rate; - info->max_fragment_size = slave_info.max_fragment_size * rate / slave_rate; - info->fragment_align = slave_info.fragment_align * rate / slave_rate; + info->buffer_size = muldiv64(slave_info.buffer_size, crate, srate); + info->min_fragment_size = muldiv64(slave_info.min_fragment_size, crate, srate); + info->max_fragment_size = muldiv64(slave_info.max_fragment_size, crate, srate); + info->fragment_align = muldiv64(slave_info.fragment_align, crate, srate); + if (sformat != info->req.format.sfmt || + (unsigned int) srate != info->req.format.rate || + schannels != info->req.format.channels) + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } -static int snd_pcm_plug_action(snd_pcm_plug_t *plug, int action, - unsigned long data) +static void snd_pcm_plug_clear(snd_pcm_t *pcm) { - int err; - snd_pcm_plugin_t *plugin = plug->first; - while (plugin) { - if (plugin->action) { - if ((err = plugin->action(plugin, action, data))<0) - return err; - } - plugin = plugin->next; + snd_pcm_plug_t *plug = pcm->private; + snd_pcm_t *slave = plug->req_slave; + /* Clear old plugins */ + if (plug->slave != slave) { + snd_pcm_close(plug->slave); + plug->slave = slave; + pcm->fast_ops = slave->fast_ops; + pcm->fast_op_arg = slave->fast_op_arg; } - return 0; } -static int snd_pcm_plug_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) +static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) { - int err; snd_pcm_plug_t *plug = pcm->private; - - err = snd_pcm_setup(plug->slave, setup); - if (err < 0) - return err; - if (!plug->first) + int err; + assert(snd_pcm_format_linear(slv->sfmt)); + if (clt->rate == slv->rate) return 0; - setup->boundary /= setup->frag_size; - setup->frag_size = snd_pcm_plug_client_size(plug, setup->frag_size); - setup->boundary *= setup->frag_size; - setup->buffer_size = setup->frags * setup->frag_size; - setup->avail_min = snd_pcm_plug_client_size(plug, setup->avail_min); - setup->align = snd_pcm_plug_client_size(plug, setup->align); - setup->xrun_max = snd_pcm_plug_client_size(plug, setup->xrun_max); - setup->fill_max = snd_pcm_plug_client_size(plug, setup->fill_max); - setup->mmap_bytes = 0; - if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK) - setup->format = plug->first->src_format; - else - setup->format = plug->last->dst_format; - /* FIXME: this is not exact */ - setup->rate_master = setup->format.rate; - setup->rate_divisor = 1; - err = snd_pcm_plug_alloc(plug, setup->frag_size); + err = snd_pcm_rate_open(new, slv->sfmt, slv->rate, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - return 0; + slv->rate = clt->rate; + if (snd_pcm_format_linear(clt->sfmt)) + slv->sfmt = clt->sfmt; + return 1; } -static int snd_pcm_plug_status(snd_pcm_t *pcm, snd_pcm_status_t *status) +static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) { - int err; snd_pcm_plug_t *plug = pcm->private; - - err = snd_pcm_status(plug->slave, status); + unsigned int tt_ssize, tt_cused, tt_sused; + ttable_entry_t *ttable; + int err; + assert(snd_pcm_format_linear(slv->sfmt)); + if (clt->channels == slv->channels) + return 0; + if (clt->rate != slv->rate && + clt->channels > slv->channels) + return 0; + + ttable = plug->ttable; + if (ttable) { + tt_ssize = plug->tt_ssize; + tt_cused = plug->tt_cused; + tt_sused = plug->tt_sused; + } else { + unsigned int k; + unsigned int c = 0, s = 0; + int n; + tt_ssize = slv->channels; + tt_cused = clt->channels; + tt_sused = slv->channels; + ttable = alloca(tt_cused * tt_sused * sizeof(*ttable)); + for (k = 0; k < tt_cused * tt_sused; ++k) + ttable[k] = 0; + if (clt->channels > slv->channels) { + n = clt->channels; + } else { + n = slv->channels; + } + while (n-- > 0) { + ttable_entry_t v = FULL; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK && + clt->channels > slv->channels) { + int srcs = clt->channels / slv->channels; + if (s < clt->channels % slv->channels) + srcs++; + v /= srcs; + } else if (pcm->stream == SND_PCM_STREAM_CAPTURE && + slv->channels > clt->channels) { + int srcs = slv->channels / clt->channels; + if (s < slv->channels % clt->channels) + srcs++; + v /= srcs; + } + ttable[c * tt_ssize + s] = v; + if (++c == clt->channels) + c = 0; + if (++s == slv->channels) + s = 0; + } + } + err = snd_pcm_route_open(new, slv->sfmt, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - - status->hw_ptr = snd_pcm_plug_client_size(plug, status->hw_ptr); - status->appl_ptr = snd_pcm_plug_client_size(plug, status->appl_ptr); - status->avail = snd_pcm_plug_client_size(plug, status->avail); - status->avail_max = snd_pcm_plug_client_size(plug, status->avail_max); - return 0; -} - -static int snd_pcm_plug_state(snd_pcm_t *pcm) -{ - snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_state(plug->slave); + slv->channels = clt->channels; + if (snd_pcm_format_linear(clt->sfmt)) + slv->sfmt = clt->sfmt; + return 1; } -static ssize_t snd_pcm_plug_hw_ptr(snd_pcm_t *pcm, int update) +static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv) { snd_pcm_plug_t *plug = pcm->private; - ssize_t hw_ptr = snd_pcm_hw_ptr(plug->slave, update); - if (hw_ptr < 0) - return hw_ptr; - return snd_pcm_plug_client_size(plug, hw_ptr); -} - -static int snd_pcm_plug_prepare(snd_pcm_t *pcm) -{ - snd_pcm_plug_t *plug = pcm->private; - int err; - err = snd_pcm_prepare(plug->slave); + int err, cfmt; + int (*f)(snd_pcm_t **handle, int sformat, snd_pcm_t *slave, int close_slave); + if (snd_pcm_format_linear(slv->sfmt)) { + /* Conversion is done in another plugin */ + if (clt->sfmt == slv->sfmt || + clt->rate != slv->rate || + clt->channels != slv->channels) + return 0; + } else { + /* No conversion is needed */ + if (clt->sfmt == slv->sfmt && + clt->rate == slv->rate && + clt->channels == clt->channels) + return 0; + } + if (snd_pcm_format_linear(slv->sfmt)) { + cfmt = clt->sfmt; + switch (clt->sfmt) { + case SND_PCM_SFMT_MU_LAW: + f = snd_pcm_mulaw_open; + break; + case SND_PCM_SFMT_A_LAW: + f = snd_pcm_alaw_open; + break; + case SND_PCM_SFMT_IMA_ADPCM: + f = snd_pcm_adpcm_open; + break; + default: + assert(snd_pcm_format_linear(clt->sfmt)); + f = snd_pcm_linear_open; + break; + } + } else { + switch (slv->sfmt) { + case SND_PCM_SFMT_MU_LAW: + f = snd_pcm_mulaw_open; + break; + case SND_PCM_SFMT_A_LAW: + f = snd_pcm_alaw_open; + break; + case SND_PCM_SFMT_IMA_ADPCM: + f = snd_pcm_adpcm_open; + break; + default: + assert(0); + return -EINVAL; + } + if (snd_pcm_format_linear(clt->sfmt)) + cfmt = clt->sfmt; + else + cfmt = SND_PCM_SFMT_S16; + } + err = f(new, slv->sfmt, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; - if ((err = snd_pcm_plug_action(plug, PREPARE, 0))<0) - return err; - return 0; + slv->sfmt = cfmt; + return 1; } -static int snd_pcm_plug_go(snd_pcm_t *pcm) +static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, + snd_pcm_format_t *client_fmt, + snd_pcm_format_t *slave_fmt) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_go(plug->slave); + int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *s, snd_pcm_format_t *d) = { + snd_pcm_plug_change_format, + snd_pcm_plug_change_channels, + snd_pcm_plug_change_rate, + snd_pcm_plug_change_channels, + snd_pcm_plug_change_format + }; + snd_pcm_format_t sfmt = *slave_fmt; + unsigned int k = 0; + while (1) { + snd_pcm_t *new; + int err; + if (client_fmt->sfmt == sfmt.sfmt && + client_fmt->channels == sfmt.channels && + client_fmt->rate == sfmt.rate) + return 0; + assert(k < sizeof(funcs)/sizeof(*funcs)); + err = funcs[k](pcm, &new, client_fmt, &sfmt); + if (err < 0) { + snd_pcm_plug_clear(pcm); + return err; + } + if (err) { + plug->slave = new; + pcm->fast_ops = new->fast_ops; + pcm->fast_op_arg = new->fast_op_arg; + } + k++; + } + assert(0); + return 0; } -static int snd_pcm_plug_drain(snd_pcm_t *pcm) +static int snd_pcm_plug_params(snd_pcm_t *pcm, snd_pcm_params_t *params) { snd_pcm_plug_t *plug = pcm->private; + snd_pcm_t *slave = plug->req_slave; + snd_pcm_format_t *slave_format, *format; + snd_pcm_params_info_t slave_info; + int srate; int err; - - if ((err = snd_pcm_drain(plug->slave)) < 0) - return err; - if ((err = snd_pcm_plug_action(plug, DRAIN, 0))<0) + + memset(&slave_info, 0, sizeof(slave_info)); + err = snd_pcm_params_info(slave, &slave_info); + if (err < 0) return err; - return 0; -} -static int snd_pcm_plug_flush(snd_pcm_t *pcm) -{ - snd_pcm_plug_t *plug = pcm->private; - int err; + slave_info.req = *params; + format = ¶ms->format; + slave_format = &slave_info.req.format; - if ((err = snd_pcm_flush(plug->slave)) < 0) - return err; - if ((err = snd_pcm_plug_action(plug, FLUSH, 0))<0) - return err; - return 0; -} + if ((slave_info.formats & (1 << format->sfmt)) == 0) { + int slave_fmt = snd_pcm_plug_slave_fmt(format->sfmt, &slave_info); + if (slave_fmt < 0) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return slave_fmt; + } + slave_format->sfmt = slave_fmt; + } -static int snd_pcm_plug_pause(snd_pcm_t *pcm, int enable) -{ - snd_pcm_plug_t *plug = pcm->private; - int err; - - if ((err = snd_pcm_pause(plug->slave, enable)) < 0) - return err; - if ((err = snd_pcm_plug_action(plug, PAUSE, 0))<0) + if (format->channels < slave_info.min_channels) + slave_format->channels = slave_info.min_channels; + else if (format->channels > slave_info.max_channels) + slave_format->channels = slave_info.max_channels; + + srate = snd_pcm_plug_slave_rate(format->rate, &slave_info); + if (srate < 0) { + params->fail_mask = SND_PCM_PARAMS_RATE; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return srate; + } + slave_format->rate = srate; + + slave_info.req_mask = ~0; + err = snd_pcm_params_info(slave, &slave_info); + if (err < 0) { + params->fail_mask = slave_info.req.fail_mask; + params->fail_reason = slave_info.req.fail_reason; return err; - return 0; -} + } -static int snd_pcm_plug_channel_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_info_t *info ATTRIBUTE_UNUSED) -{ - /* FIXME: if route plugin is not inserted or its ttable is trivial - this should be implemented */ - return -ENOSYS; -} + if (slave_format->rate - slave_info.min_rate < slave_info.max_rate - slave_format->rate) + slave_format->rate = slave_info.min_rate; + else + slave_format->rate = slave_info.max_rate; -static int snd_pcm_plug_channel_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_params_t *params ATTRIBUTE_UNUSED) -{ - /* FIXME: if route plugin is not inserted or its ttable is trivial - this should be implemented */ - return -ENOSYS; -} + err = snd_pcm_plug_insert_plugins(pcm, format, slave_format); + if (err < 0) + return err; + slave = plug->slave; -static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_setup_t *setup ATTRIBUTE_UNUSED) -{ - /* FIXME: if route plugin is not inserted or its ttable is trivial - this should be implemented for non mmap setups */ - return -ENOSYS; + err = snd_pcm_params(slave, params); + if (err < 0) + snd_pcm_plug_clear(pcm); + return err; } -static ssize_t snd_pcm_plug_appl_ptr(snd_pcm_t *pcm, off_t offset) +static int snd_pcm_plug_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) { - ssize_t ret; snd_pcm_plug_t *plug = pcm->private; - if (offset < 0) { - offset = snd_pcm_plug_slave_size(plug, -offset); - if (offset < 0) - return offset; - offset = -offset; - } else { - offset = snd_pcm_plug_slave_size(plug, offset); - if (offset < 0) - return offset; - } - ret = snd_pcm_appl_ptr(plug->slave, offset); - if (ret < 0) - return ret; - return snd_pcm_plug_client_size(plug, ret); -} - -ssize_t snd_pcm_plug_writev(snd_pcm_t *pcm, snd_timestamp_t *tstamp ATTRIBUTE_UNUSED, const struct iovec *vector, unsigned long count) -{ - snd_pcm_plug_t *plug = pcm->private; - snd_pcm_t *handle = plug->handle; - unsigned int k, step; - size_t result = 0; - assert(plug->frames_alloc); - if (handle->setup.format.interleave) - step = 1; - else - step = handle->setup.format.channels; - for (k = 0; k < count; k += step) { - snd_pcm_plugin_channel_t *channels; - ssize_t frames; - frames = snd_pcm_plug_client_channels_iovec(plug, vector, step, &channels); - if (frames < 0) { - if (result > 0) - return result; - return frames; - } - while (1) { - unsigned int c; - ssize_t ret; - size_t frames1 = frames; - if (frames1 > plug->frames_alloc) - frames1 = plug->frames_alloc; - ret = snd_pcm_plug_write_transfer(plug, channels, frames1); - if (ret < 0) { - if (result > 0) - return result; - return ret; - } - result += ret; - frames -= ret; - if (frames == 0) - break; - for (c = 0; c < handle->setup.format.channels; ++c) - channels[c].area.addr += ret * channels[c].area.step / 8; - } - vector += step; - } - return result; + return snd_pcm_setup(plug->slave, setup); } -ssize_t snd_pcm_plug_readv(snd_pcm_t *pcm, snd_timestamp_t *tstamp ATTRIBUTE_UNUSED, const struct iovec *vector, unsigned long count) +static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { snd_pcm_plug_t *plug = pcm->private; - snd_pcm_t *handle = plug->handle; - unsigned int k, step; - size_t result = 0; - assert(plug->frames_alloc); - if (handle->setup.format.interleave) - step = 1; - else - step = handle->setup.format.channels; - for (k = 0; k < count; k += step) { - snd_pcm_plugin_channel_t *channels; - ssize_t frames; - frames = snd_pcm_plug_client_channels_iovec(plug, vector, step, &channels); - if (frames < 0) { - if (result > 0) - return result; - return frames; - } - while (1) { - unsigned int c; - ssize_t ret; - size_t frames1 = frames; - if (frames1 > plug->frames_alloc) - frames1 = plug->frames_alloc; - ret = snd_pcm_plug_read_transfer(plug, channels, frames1); - if (ret < 0) { - if (result > 0) - return result; - return ret; - } - result += ret; - frames -= ret; - if (frames == 0) - break; - for (c = 0; c < handle->setup.format.channels; ++c) - channels[c].area.addr += ret * channels[c].area.step / 8; - } - vector += step; - } - return result; + return snd_pcm_channel_info(plug->slave, info); } -ssize_t snd_pcm_plug_write(snd_pcm_t *pcm, snd_timestamp_t *tstamp ATTRIBUTE_UNUSED, const void *buf, size_t count) +static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) { snd_pcm_plug_t *plug = pcm->private; - snd_pcm_t *handle = plug->handle; - ssize_t frames; - snd_pcm_plugin_channel_t *channels; - size_t size = 0; - assert(plug->frames_alloc); - frames = snd_pcm_plug_client_channels_buf(plug, (char *)buf, count, &channels); - if (frames < 0) - return frames; - - while (1) { - unsigned int c; - ssize_t ret; - size_t frames1 = frames; - if (frames1 > plug->frames_alloc) - frames1 = plug->frames_alloc; - ret = snd_pcm_plug_write_transfer(plug, channels, frames1); - if (ret < 0) { - if (size > 0) - return size; - return ret; - } - size += ret; - frames -= ret; - if (frames == 0) - break; - for (c = 0; c < handle->setup.format.channels; ++c) - channels[c].area.addr += ret * channels[c].area.step / 8; - } - return size; + return snd_pcm_channel_params(plug->slave, params); } -ssize_t snd_pcm_plug_read(snd_pcm_t *pcm, snd_timestamp_t *tstamp ATTRIBUTE_UNUSED, void *buf, size_t count) +static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) { snd_pcm_plug_t *plug = pcm->private; - snd_pcm_t *handle = plug->handle; - ssize_t frames; - snd_pcm_plugin_channel_t *channels; - size_t size = 0; - assert(plug->frames_alloc); - frames = snd_pcm_plug_client_channels_buf(plug, buf, count, &channels); - if (frames < 0) - return frames; - - while (1) { - unsigned int c; - ssize_t ret; - size_t frames1 = frames; - if (frames1 > plug->frames_alloc) - frames1 = plug->frames_alloc; - ret = snd_pcm_plug_read_transfer(plug, channels, frames1); - if (ret < 0) { - if (size > 0) - return size; - return ret; - } - size += ret; - frames -= ret; - if (frames == 0) - break; - for (c = 0; c < handle->setup.format.channels; ++c) - channels[c].area.addr += ret * channels[c].area.step / 8; - } - return size; + return snd_pcm_channel_setup(plug->slave, setup); } -static int snd_pcm_plug_mmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t **status ATTRIBUTE_UNUSED) +static int snd_pcm_plug_mmap_status(snd_pcm_t *pcm) { - return -EBADFD; + snd_pcm_plug_t *plug = pcm->private; + return snd_pcm_mmap_status(plug->slave, NULL); } -static int snd_pcm_plug_mmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t **control ATTRIBUTE_UNUSED) +static int snd_pcm_plug_mmap_control(snd_pcm_t *pcm) { - return -EBADFD; + snd_pcm_plug_t *plug = pcm->private; + return snd_pcm_mmap_control(plug->slave, NULL); } -static int snd_pcm_plug_mmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void **buffer ATTRIBUTE_UNUSED, size_t bsize ATTRIBUTE_UNUSED) +static int snd_pcm_plug_mmap_data(snd_pcm_t *pcm) { - return -EBADFD; + snd_pcm_plug_t *plug = pcm->private; + return snd_pcm_mmap_data(plug->slave, NULL); } -static int snd_pcm_plug_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_status_t *status ATTRIBUTE_UNUSED) -{ - return -EBADFD; -} - -static int snd_pcm_plug_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_mmap_control_t *control ATTRIBUTE_UNUSED) +static int snd_pcm_plug_munmap_status(snd_pcm_t *pcm) { - return -EBADFD; -} - -static int snd_pcm_plug_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED, void *buffer ATTRIBUTE_UNUSED, size_t size ATTRIBUTE_UNUSED) -{ - return -EBADFD; + snd_pcm_plug_t *plug = pcm->private; + return snd_pcm_munmap_status(plug->slave); } -static int snd_pcm_plug_channels_mask(snd_pcm_t *pcm, - bitset_t *client_vmask) +static int snd_pcm_plug_munmap_control(snd_pcm_t *pcm) { snd_pcm_plug_t *plug = pcm->private; - if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK) - return snd_pcm_plug_playback_channels_mask(plug, client_vmask); - else - return snd_pcm_plug_capture_channels_mask(plug, client_vmask); + return snd_pcm_munmap_control(plug->slave); } - -int snd_pcm_plug_file_descriptor(snd_pcm_t *pcm) + +static int snd_pcm_plug_munmap_data(snd_pcm_t *pcm) { snd_pcm_plug_t *plug = pcm->private; - return snd_pcm_file_descriptor(plug->slave); + return snd_pcm_munmap_data(plug->slave); } - + static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp) { snd_pcm_plug_t *plug = pcm->private; - snd_pcm_t *handle = plug->handle; - snd_pcm_plugin_t *plugin; - if (!plug->first) { - fprintf(fp, "Plug PCM -> "); - snd_pcm_dump(plug->slave, fp); - return; - } - fprintf(fp, "Plug PCM\n"); - if (handle->valid_setup) { - fprintf(fp, "\nIts setup is:\n"); - snd_pcm_dump_setup(handle, fp); - } - fprintf(fp, "\nPlugins:\n"); - plugin = plug->first; - while (plugin) { - snd_pcm_plugin_dump(plugin, fp); - plugin = plugin->next; - } - fprintf(fp, "\n"); + fprintf(fp, "Plug PCM: "); + snd_pcm_dump(plug->slave, fp); } -static int snd_pcm_plug_params(snd_pcm_t *pcm, snd_pcm_params_t *params); - struct snd_pcm_ops snd_pcm_plug_ops = { close: snd_pcm_plug_close, info: snd_pcm_plug_info, params_info: snd_pcm_plug_params_info, params: snd_pcm_plug_params, setup: snd_pcm_plug_setup, - dump: snd_pcm_plug_dump, -}; - -struct snd_pcm_fast_ops snd_pcm_plug_fast_ops = { - nonblock: snd_pcm_plug_nonblock, channel_info: snd_pcm_plug_channel_info, channel_params: snd_pcm_plug_channel_params, channel_setup: snd_pcm_plug_channel_setup, - status: snd_pcm_plug_status, - hw_ptr: snd_pcm_plug_hw_ptr, - state: snd_pcm_plug_state, - prepare: snd_pcm_plug_prepare, - go: snd_pcm_plug_go, - drain: snd_pcm_plug_drain, - flush: snd_pcm_plug_flush, - pause: snd_pcm_plug_pause, - appl_ptr: snd_pcm_plug_appl_ptr, - write: snd_pcm_plug_write, - writev: snd_pcm_plug_writev, - read: snd_pcm_plug_read, - readv: snd_pcm_plug_readv, + dump: snd_pcm_plug_dump, + nonblock: snd_pcm_plug_nonblock, mmap_status: snd_pcm_plug_mmap_status, mmap_control: snd_pcm_plug_mmap_control, mmap_data: snd_pcm_plug_mmap_data, munmap_status: snd_pcm_plug_munmap_status, munmap_control: snd_pcm_plug_munmap_control, munmap_data: snd_pcm_plug_munmap_data, - file_descriptor: snd_pcm_plug_file_descriptor, - channels_mask: snd_pcm_plug_channels_mask, }; -static void snd_pcm_plug_slave_params(snd_pcm_plug_t *plug, - snd_pcm_params_t *params, - snd_pcm_params_t *slave_params) -{ - /* compute right sizes */ - slave_params->frag_size = snd_pcm_plug_slave_size(plug, params->frag_size); - slave_params->buffer_size = snd_pcm_plug_slave_size(plug, params->buffer_size); - slave_params->fill_max = snd_pcm_plug_slave_size(plug, params->fill_max); - slave_params->avail_min = snd_pcm_plug_slave_size(plug, params->avail_min); - slave_params->xrun_max = snd_pcm_plug_slave_size(plug, params->xrun_max); - slave_params->align = snd_pcm_plug_slave_size(plug, params->align); - if (slave_params->boundary == 0 || slave_params->boundary > LONG_MAX) - slave_params->boundary = LONG_MAX; - assert(params->buffer_size > 0); - slave_params->boundary /= params->buffer_size; - if (slave_params->boundary > LONG_MAX / slave_params->buffer_size) - slave_params->boundary = LONG_MAX; - else - slave_params->boundary *= slave_params->buffer_size; -} - - - -static int snd_pcm_plug_params(snd_pcm_t *pcm, snd_pcm_params_t *params) -{ - snd_pcm_params_t slave_params; - snd_pcm_info_t slave_info; - snd_pcm_format_t *req_format, *real_format, format1; - snd_pcm_params_info_t slave_params_info; - snd_pcm_plugin_t *plugin; - snd_pcm_plug_t *plug; - int err; - int first = 1; - - plug = pcm->private; - - /* - * try to decide, if a conversion is required - */ - - memset(&slave_info, 0, sizeof(slave_info)); - if ((err = snd_pcm_info(plug->slave, &slave_info)) < 0) { - snd_pcm_plug_clear(plug); - return err; - } - memset(&slave_params_info, 0, sizeof(slave_params_info)); - if ((err = snd_pcm_params_info(plug->slave, &slave_params_info)) < 0) { - snd_pcm_plug_clear(plug); - return err; - } - - slave_params = *params; - if ((err = snd_pcm_plug_slave_format(¶ms->format, &slave_info, &slave_params_info, &slave_params.format)) < 0) - return err; - - retry: - /* add necessary plugins */ - format1 = params->format; - snd_pcm_plug_clear(plug); - if ((err = snd_pcm_plug_format_plugins(plug, &format1, - &slave_params.format)) < 0) - return err; - - /* compute right sizes */ - snd_pcm_plug_slave_params(plug, params, &slave_params); - - pdprintf("params requested params: format = %i, rate = %i, channels = %i\n", slave_params.format.format, slave_params.format.rate, slave_params.format.channels); - - err = snd_pcm_params(plug->slave, &slave_params); - if (err < 0) { - params->fail_mask = slave_params.fail_mask; - params->fail_reason = slave_params.fail_reason; - return err; - } - req_format = &slave_params.format; - real_format = &plug->slave->setup.format; - if (real_format->interleave != req_format->interleave || - real_format->format != req_format->format || - real_format->rate != req_format->rate || - real_format->channels != req_format->channels) { - assert(first); - slave_params.format = *real_format; - first = 0; - goto retry; - } - - if (!plug->first) { - *plug->handle->fast_ops = *plug->slave->fast_ops; - plug->handle->fast_op_arg = plug->slave->fast_op_arg; - return 0; - } - - *plug->handle->fast_ops = snd_pcm_plug_fast_ops; - plug->handle->fast_op_arg = pcm; - - /* - * I/O plugins - */ - - if (slave_info.flags & SND_PCM_INFO_MMAP) { - pdprintf("params mmap plugin\n"); - err = snd_pcm_plugin_build_mmap(plug, &slave_params.format, &plugin); - } else { - pdprintf("params I/O plugin\n"); - err = snd_pcm_plugin_build_io(plug, &slave_params.format, &plugin); - } - if (err < 0) - return err; - if (plug->slave->stream == SND_PCM_STREAM_PLAYBACK) { - err = snd_pcm_plugin_append(plugin); - } else { - err = snd_pcm_plugin_insert(plugin); - } - if (err < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - - err = snd_pcm_plug_action(plug, INIT, 0); - if (err < 0) - return err; - return 0; -} - -int snd_pcm_plug_create(snd_pcm_t **handlep, snd_pcm_t *slave, int close_slave) +int snd_pcm_plug_open(snd_pcm_t **handlep, + ttable_entry_t *ttable, + unsigned int tt_ssize, + unsigned int tt_cused, unsigned int tt_sused, + snd_pcm_t *slave, int close_slave) { snd_pcm_t *handle; snd_pcm_plug_t *plug; + int err; assert(handlep && slave); - handle = calloc(1, sizeof(snd_pcm_t)); - if (!handle) - return -ENOMEM; plug = calloc(1, sizeof(snd_pcm_plug_t)); - if (!plug) { - free(handle); + if (!plug) return -ENOMEM; - } - plug->handle = handle; - plug->slave = slave; + plug->slave = plug->req_slave = slave; plug->close_slave = close_slave; + plug->ttable = ttable; + plug->tt_ssize = tt_ssize; + plug->tt_cused = tt_cused; + plug->tt_sused = tt_sused; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(plug); + return -ENOMEM; + } handle->type = SND_PCM_TYPE_PLUG; handle->stream = slave->stream; handle->ops = &snd_pcm_plug_ops; handle->op_arg = handle; - handle->fast_ops = malloc(sizeof(*handle->fast_ops)); - *handle->fast_ops = snd_pcm_plug_fast_ops; - handle->fast_op_arg = handle; + handle->fast_ops = slave->fast_ops; + handle->fast_op_arg = slave->fast_op_arg; handle->mode = slave->mode; handle->private = plug; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } *handlep = handle; + return 0; } @@ -843,12 +688,70 @@ int snd_pcm_plug_open_subdevice(snd_pcm_t **handlep, int card, int device, int s err = snd_pcm_hw_open_subdevice(&slave, card, device, subdevice, stream, mode); if (err < 0) return err; - return snd_pcm_plug_create(handlep, slave, 1); + return snd_pcm_plug_open(handlep, 0, 0, 0, 0, slave, 1); } -int snd_pcm_plug_open(snd_pcm_t **handlep, int card, int device, int stream, int mode) +int snd_pcm_plug_open_card(snd_pcm_t **handlep, int card, int device, int stream, int mode) { return snd_pcm_plug_open_subdevice(handlep, card, device, -1, stream, mode); } +#define MAX_CHANNELS 32 +int _snd_pcm_plug_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + snd_config_t *tt = NULL; + ttable_entry_t *ttable = NULL; + unsigned int cused, sused; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "ttable") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + tt = n; + continue; + } + return -EINVAL; + } + if (!sname) + return -EINVAL; + if (tt) { + ttable = malloc(MAX_CHANNELS * MAX_CHANNELS * sizeof(*ttable)); + err = snd_pcm_route_load_ttable(tt, ttable, MAX_CHANNELS, MAX_CHANNELS, + &cused, &sused, -1); + if (err < 0) + return err; + } + + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_plug_open(pcmp, ttable, MAX_CHANNELS, cused, sused, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + diff --git a/src/pcm/pcm_plugin.c b/src/pcm/pcm_plugin.c new file mode 100644 index 00000000..28257038 --- /dev/null +++ b/src/pcm/pcm_plugin.c @@ -0,0 +1,394 @@ +/* + * PCM - Common plugin code + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 "pcm_local.h" +#include "pcm_plugin.h" +#include <limits.h> + +int snd_pcm_plugin_close(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + int err = 0; + if (plugin->close_slave) + err = snd_pcm_close(plugin->slave); + free(plugin); + return 0; +} + +int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_nonblock(plugin->slave, nonblock); +} + +int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_info(plugin->slave, info); +} + +int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_channel_info(plugin->slave, info); +} + +int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_channel_params(plugin->slave, params); +} + +int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +{ + snd_pcm_plugin_t *plugin = pcm->private; + int err; + err = snd_pcm_channel_setup(plugin->slave, setup); + if (err < 0) + return err; + if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) { + setup->area.addr = pcm->mmap_data; + setup->area.first = setup->channel * pcm->bits_per_sample; + setup->area.step = pcm->bits_per_frame; + } else { + setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; + setup->area.first = 0; + setup->area.step = pcm->bits_per_sample; + } + return 0; +} + +int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status) +{ + snd_pcm_plugin_t *plugin = pcm->private; + int err = snd_pcm_status(plugin->slave, status); + if (err < 0) + return err; + status->hw_ptr = plugin->mmap_status.hw_ptr; + status->appl_ptr = plugin->mmap_control.appl_ptr; + status->avail = (pcm->stream == SND_PCM_STREAM_PLAYBACK ? + snd_pcm_mmap_playback_avail(pcm) : + snd_pcm_mmap_capture_avail(pcm)); + return 0; +} + +int snd_pcm_plugin_state(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_state(plugin->slave); +} + +int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp) +{ + snd_pcm_plugin_t *plugin = pcm->private; + ssize_t sd; + int err = snd_pcm_delay(plugin->slave, &sd); + int d; + if (err < 0) + return err; + if (plugin->client_frames) + sd = plugin->client_frames(pcm, sd); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + d = pcm->setup.buffer_size - snd_pcm_mmap_playback_avail(pcm); + else + d = snd_pcm_mmap_capture_avail(pcm); + *delayp = sd + d; + return 0; +} + +int snd_pcm_plugin_prepare(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + int err = snd_pcm_prepare(plugin->slave); + if (err < 0) + return err; + plugin->mmap_status.hw_ptr = 0; + plugin->mmap_control.appl_ptr = 0; + if (plugin->init) { + err = plugin->init(pcm); + if (err < 0) + return err; + } + return 0; +} + +int snd_pcm_plugin_start(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_start(plugin->slave); +} + +int snd_pcm_plugin_stop(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_stop(plugin->slave); +} + +int snd_pcm_plugin_flush(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_flush(plugin->slave); +} + +int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_pause(plugin->slave, enable); +} + +ssize_t snd_pcm_plugin_appl_ptr(snd_pcm_t *pcm, off_t offset) +{ + /* FIXME */ + return -ENOSYS; +} + +ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t frames; + snd_pcm_areas_from_buf(pcm, areas, (void*)buffer); + frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t frames; + snd_pcm_areas_from_bufs(pcm, areas, bufs); + frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t frames; + snd_pcm_areas_from_buf(pcm, areas, buffer); + frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_channel_area_t areas[pcm->setup.format.channels]; + ssize_t frames; + snd_pcm_areas_from_bufs(pcm, areas, bufs); + frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t client_size) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_t *slave = plugin->slave; + size_t client_xfer = 0; + size_t slave_xfer = 0; + ssize_t err = 0; + ssize_t slave_size; + if (pcm->stream == SND_PCM_STREAM_CAPTURE) { + snd_pcm_mmap_appl_forward(pcm, client_size); + return client_size; + } + slave_size = snd_pcm_avail_update(slave); + if (slave_size <= 0) + return slave_size; + while (client_xfer < client_size && + slave_xfer < (size_t)slave_size) { + size_t slave_frames = slave_size - slave_xfer; + size_t client_frames = client_size - client_xfer; + size_t cont = pcm->setup.buffer_size - snd_pcm_mmap_hw_offset(pcm); + if (cont < client_frames) + client_frames = cont; + err = plugin->write(pcm, pcm->mmap_areas, + snd_pcm_mmap_hw_offset(pcm), + client_frames, &slave_frames); + if (err < 0) + break; + snd_pcm_mmap_appl_forward(pcm, err); + client_xfer += err; + slave_xfer += slave_frames; + } + if (client_xfer > 0) + return client_xfer; + return err; +} + +ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + snd_pcm_t *slave = plugin->slave; + size_t client_xfer; + size_t slave_xfer = 0; + ssize_t err = 0; + size_t client_size; + ssize_t slave_size = snd_pcm_avail_update(slave); + if (slave_size <= 0) + return slave_size; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK || + !pcm->mmap_data) + return plugin->client_frames ? + plugin->client_frames(pcm, slave_size) : slave_size; + client_xfer = snd_pcm_mmap_capture_avail(pcm); + client_size = pcm->setup.buffer_size; + while (slave_xfer < (size_t)slave_size && + client_xfer < client_size) { + size_t slave_frames = slave_size - slave_xfer; + size_t client_frames = client_size - client_xfer; + size_t cont = pcm->setup.buffer_size - snd_pcm_mmap_hw_offset(pcm); + if (cont < client_frames) + client_frames = cont; + err = plugin->read(pcm, pcm->mmap_areas, + snd_pcm_mmap_hw_offset(pcm), + client_frames, &slave_frames); + if (err < 0) + break; + client_xfer += err; + slave_xfer += slave_frames; + } + if (client_xfer > 0) + return client_xfer; + return err; +} + +int snd_pcm_plugin_mmap_status(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + pcm->mmap_status = &plugin->mmap_status; + return 0; +} + +int snd_pcm_plugin_mmap_control(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + pcm->mmap_control = &plugin->mmap_control; + return 0; +} + +int snd_pcm_plugin_mmap_data(snd_pcm_t *pcm) +{ + void *ptr = malloc(snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size)); + if (!ptr) + return -ENOMEM; + pcm->mmap_data = ptr; + return 0; +} + +int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + +int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + return 0; +} + +int snd_pcm_plugin_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED) +{ + free(pcm->mmap_data); + return 0; +} + +int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_poll_descriptor(plugin->slave); +} + +int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask) +{ + snd_pcm_plugin_t *plugin = pcm->private; + return snd_pcm_channels_mask(plugin->slave, cmask); +} + +int conv_index(int src_format, int dst_format) +{ + int src_endian, dst_endian, sign, src_width, dst_width; + + sign = (snd_pcm_format_signed(src_format) != + snd_pcm_format_signed(dst_format)); +#ifdef SND_LITTLE_ENDIAN + src_endian = snd_pcm_format_big_endian(src_format); + dst_endian = snd_pcm_format_big_endian(dst_format); +#else + src_endian = snd_pcm_format_little_endian(src_format); + dst_endian = snd_pcm_format_little_endian(dst_format); +#endif + + if (src_endian < 0) + src_endian = 0; + if (dst_endian < 0) + dst_endian = 0; + + src_width = snd_pcm_format_width(src_format) / 8 - 1; + dst_width = snd_pcm_format_width(dst_format) / 8 - 1; + + return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; +} + +int getput_index(int format) +{ + int sign, width, endian; + sign = !snd_pcm_format_signed(format); + width = snd_pcm_format_width(format) / 8 - 1; +#ifdef SND_LITTLE_ENDIAN + endian = snd_pcm_format_big_endian(format); +#else + endian = snd_pcm_format_little_endian(format); +#endif + if (endian < 0) + endian = 0; + return width * 4 + endian * 2 + sign; +} + +struct snd_pcm_fast_ops snd_pcm_plugin_fast_ops = { + status: snd_pcm_plugin_status, + state: snd_pcm_plugin_state, + delay: snd_pcm_plugin_delay, + prepare: snd_pcm_plugin_prepare, + start: snd_pcm_plugin_start, + stop: snd_pcm_plugin_stop, + flush: snd_pcm_plugin_flush, + pause: snd_pcm_plugin_pause, + appl_ptr: snd_pcm_plugin_appl_ptr, + writei: snd_pcm_plugin_writei, + writen: snd_pcm_plugin_writen, + readi: snd_pcm_plugin_readi, + readn: snd_pcm_plugin_readn, + poll_descriptor: snd_pcm_plugin_poll_descriptor, + channels_mask: snd_pcm_plugin_channels_mask, + avail_update: snd_pcm_plugin_avail_update, + mmap_forward: snd_pcm_plugin_mmap_forward, +}; + diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h new file mode 100644 index 00000000..e76993ca --- /dev/null +++ b/src/pcm/pcm_plugin.h @@ -0,0 +1,105 @@ +/* + * PCM - Common plugin code + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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. + * + */ + +typedef struct { + snd_pcm_t *slave; + int close_slave; + snd_pcm_xfer_areas_func_t read; + snd_pcm_xfer_areas_func_t write; + size_t (*client_frames)(snd_pcm_t *pcm, size_t frames); + int (*init)(snd_pcm_t *pcm); + snd_pcm_mmap_control_t mmap_control; + snd_pcm_mmap_status_t mmap_status; +} snd_pcm_plugin_t; + +int snd_pcm_plugin_close(snd_pcm_t *pcm); +int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock); +int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info); +int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info); +int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params); +int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup); +int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status); +int snd_pcm_plugin_state(snd_pcm_t *pcm); +int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp); +int snd_pcm_plugin_prepare(snd_pcm_t *pcm); +int snd_pcm_plugin_start(snd_pcm_t *pcm); +int snd_pcm_plugin_stop(snd_pcm_t *pcm); +int snd_pcm_plugin_flush(snd_pcm_t *pcm); +int snd_pcm_plugin_pause(snd_pcm_t *pcm, int enable); +ssize_t snd_pcm_plugin_appl_ptr(snd_pcm_t *pcm, off_t offset); +ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size); +ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size); +ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size); +ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size); +ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t size); +ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm); +int snd_pcm_plugin_mmap_status(snd_pcm_t *pcm); +int snd_pcm_plugin_mmap_control(snd_pcm_t *pcm); +int snd_pcm_plugin_mmap_data(snd_pcm_t *pcm); +int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm); +int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm); +int snd_pcm_plugin_munmap_data(snd_pcm_t *pcm); +int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm); +int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask); +int getput_index(int format); +int conv_index(int src_format, int dst_format); + +#define SND_PCM_LINEAR_FORMATS (SND_PCM_FMT_S8 | SND_PCM_FMT_U8 | \ + SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE | \ + SND_PCM_FMT_U16_LE | SND_PCM_FMT_U16_BE | \ + SND_PCM_FMT_S24_LE | SND_PCM_FMT_S24_BE | \ + SND_PCM_FMT_U24_LE | SND_PCM_FMT_U24_BE | \ + SND_PCM_FMT_S32_LE | SND_PCM_FMT_S32_BE | \ + SND_PCM_FMT_U32_LE | SND_PCM_FMT_U32_BE) + +extern struct snd_pcm_fast_ops snd_pcm_plugin_fast_ops; + +#define muldiv64(a,b,d) (((int64_t)(a) * (b) + (b) / 2) / (d)) + +#define ROUTE_PLUGIN_FLOAT 1 +#define ROUTE_PLUGIN_RESOLUTION 16 + +#if ROUTE_PLUGIN_FLOAT +typedef float ttable_entry_t; +#define HALF 0.5 +#define FULL 1.0 +#else +typedef int ttable_entry_t; +#define HALF (ROUTE_PLUGIN_RESOLUTION / 2) +#define FULL ROUTE_PLUGIN_RESOLUTION +#endif + +int snd_pcm_linear_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave); +int snd_pcm_mulaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave); +int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave); +int snd_pcm_adpcm_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave); +int snd_pcm_route_load_ttable(snd_config_t *tt, ttable_entry_t *ttable, + unsigned int tt_csize, unsigned int tt_ssize, + unsigned int *tt_cused, unsigned int *tt_sused, + int schannels); +int snd_pcm_route_open(snd_pcm_t **handlep, + int sformat, unsigned int schannels, + ttable_entry_t *ttable, + unsigned int tt_ssize, + unsigned int tt_cused, unsigned int tt_sused, + snd_pcm_t *slave, int close_slave); +int snd_pcm_rate_open(snd_pcm_t **handlep, int sformat, int srate, snd_pcm_t *slave, int close_slave); + diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c new file mode 100644 index 00000000..104019f2 --- /dev/null +++ b/src/pcm/pcm_rate.c @@ -0,0 +1,693 @@ +/* + * PCM - Rate conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <limits.h> +#include <byteswap.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +#define DIV (1<<16) + +typedef struct { + int16_t sample; + int sum; + unsigned int pos; +} rate_state_t; + +typedef size_t (*rate_f)(snd_pcm_channel_area_t *src_areas, + size_t src_offset, size_t src_frames, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, size_t *dst_framesp, + size_t channels, + int getidx, int putidx, + unsigned int arg, + rate_state_t *states); + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int get_idx; + int put_idx; + unsigned int pitch; + rate_f func; + int req_sformat; + int req_srate; + int sformat; + int cformat; + int srate; + int crate; + int cxfer_mode, cmmap_shape; + rate_state_t *states; +} snd_pcm_rate_t; + +static size_t resample_expand(snd_pcm_channel_area_t *src_areas, + size_t src_offset, size_t src_frames, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, size_t *dst_framesp, + size_t channels, + int getidx, int putidx, + unsigned int get_threshold, + rate_state_t *states) +{ +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[getidx]; + void *put = put_s16_labels[putidx]; + unsigned int channel; + size_t src_frames1 = 0; + size_t dst_frames1 = 0; + size_t dst_frames = *dst_framesp; + int16_t sample = 0; + + if (src_frames == 0 || + dst_frames == 0) + return 0; + for (channel = 0; channel < channels; ++channel) { + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; + char *src, *dst; + int src_step, dst_step; + int16_t old_sample = states->sample; + unsigned int pos = states->pos; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + src_frames1 = 0; + dst_frames1 = 0; + while (dst_frames1 < dst_frames) { + if (pos >= get_threshold) { + int16_t new_sample; + if (src_frames1 == src_frames) + break; + pos -= get_threshold; + goto *get; +#define GET_S16_END after_get +#include "plugin_ops.h" +#undef GET_S16_END + after_get: + src += src_step; + src_frames1++; + new_sample = sample; + sample = (old_sample * (DIV - pos) + new_sample * pos) / DIV; + old_sample = new_sample; + } else + sample = old_sample; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + dst_frames1++; + pos += DIV; + } + states->sample = old_sample; + states->pos = pos; + states++; + } + *dst_framesp = dst_frames1; + return src_frames1; +} + +static size_t resample_shrink(snd_pcm_channel_area_t *src_areas, + size_t src_offset, size_t src_frames, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, size_t *dst_framesp, + size_t channels, + int getidx, int putidx, + unsigned int get_increment, + rate_state_t *states) +{ +#define GET_S16_LABELS +#define PUT_S16_LABELS +#include "plugin_ops.h" +#undef GET_S16_LABELS +#undef PUT_S16_LABELS + void *get = get_s16_labels[getidx]; + void *put = put_s16_labels[putidx]; + unsigned int channel; + size_t src_frames1 = 0; + size_t dst_frames1 = 0; + size_t dst_frames = *dst_framesp; + int16_t sample = 0; + + if (src_frames == 0 || + dst_frames == 0) + return 0; + for (channel = 0; channel < channels; ++channel) { + snd_pcm_channel_area_t *src_area = &src_areas[channel]; + snd_pcm_channel_area_t *dst_area = &dst_areas[channel]; + unsigned int pos; + int sum; + char *src, *dst; + int src_step, dst_step; + sum = states->sum; + pos = states->pos; +#if 0 + if (!src_area->enabled) { + if (dst_area->wanted) + snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt); + dst_area->enabled = 0; + continue; + } + dst_area->enabled = 1; +#endif + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + src_frames1 = 0; + dst_frames1 = 0; + while (src_frames1 < src_frames) { + + goto *get; +#define GET_S16_END after_get +#include "plugin_ops.h" +#undef GET_S16_END + after_get: + src += src_step; + src_frames1++; + pos += get_increment; + if (pos >= DIV) { + int s = sample; + pos -= DIV; + sum += s * (get_increment - pos); + sum /= DIV; + sample = sum; + goto *put; +#define PUT_S16_END after_put +#include "plugin_ops.h" +#undef PUT_S16_END + after_put: + dst += dst_step; + sum = s * pos; + dst_frames1++; + if (dst_frames1 == dst_frames) + break; + } else + sum += sample * get_increment; + } + states->sum = sum; + states->pos = pos; + states++; + } + *dst_framesp = dst_frames1; + return src_frames1; +} + +static int snd_pcm_rate_close(snd_pcm_t *pcm) +{ + snd_pcm_rate_t *rate = pcm->private; + int err = 0; + if (rate->plug.close_slave) + err = snd_pcm_close(rate->plug.slave); + if (rate->states) + free(rate->states); + free(rate); + return 0; +} + +static int snd_pcm_rate_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_rate_t *rate = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + unsigned int crate = info->req.format.rate; + unsigned int srate; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT && + !snd_pcm_format_linear(sfmt)) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (rate->req_sformat >= 0) { + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req.format.sfmt = rate->req_sformat; + } + info->req_mask |= SND_PCM_PARAMS_RATE; + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + info->req.format.rate = rate->req_srate; + err = snd_pcm_params_info(rate->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + info->req.format.rate = crate; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = SND_PCM_LINEAR_FORMATS; + if (!(req_mask & SND_PCM_PARAMS_RATE)) { + info->min_rate = 4000; + info->max_rate = 192000; + return 0; + } + if (rate->req_srate - info->min_rate < info->max_rate - rate->req_srate) + srate = info->min_rate; + else + srate = info->max_rate; + info->min_rate = crate; + info->max_rate = crate; + if (info->buffer_size) + info->buffer_size = muldiv64(info->buffer_size, crate, srate); + if (info->min_fragment_size) + info->min_fragment_size = muldiv64(info->min_fragment_size, crate, srate); + if (info->max_fragment_size) + info->max_fragment_size = muldiv64(info->max_fragment_size, crate, srate); + if (info->fragment_align) + info->fragment_align = muldiv64(info->fragment_align, crate, srate); + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return 0; +} + +static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_rate_t *rate = pcm->private; + snd_pcm_t *slave = rate->plug.slave; + snd_pcm_params_t slave_params; + snd_pcm_params_info_t slave_info; + int srate, crate; + int err; + if (!snd_pcm_format_linear(params->format.sfmt)) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + slave_params = *params; + rate->cformat = params->format.sfmt; + rate->crate = crate = params->format.rate; + rate->cxfer_mode = params->xfer_mode; + rate->cmmap_shape = params->mmap_shape; + + memset(&slave_info, 0, sizeof(slave_info)); + slave_info.req = *params; + if (rate->req_sformat >= 0) + slave_info.req.format.sfmt = rate->req_sformat; + slave_info.req.format.rate = rate->req_srate; + slave_info.req_mask = ~0; + err = snd_pcm_params_info(slave, &slave_info); + if (err < 0) { + params->fail_mask = slave_info.req.fail_mask; + params->fail_reason = slave_info.req.fail_reason; + return err; + } + + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + + if (rate->req_srate - slave_info.min_rate < slave_info.max_rate - rate->req_srate) + srate = slave_info.min_rate; + else + srate = slave_info.max_rate; + + slave_params.format.rate = srate; + slave_params.avail_min = muldiv64(params->avail_min, srate, crate); + slave_params.xfer_min = muldiv64(params->xfer_min, srate, crate); + slave_params.buffer_size = muldiv64(params->buffer_size, srate, crate); + slave_params.frag_size = muldiv64(params->frag_size, srate, crate); + slave_params.xfer_align = muldiv64(params->xfer_align, srate, crate); + slave_params.xrun_max = muldiv64(params->xrun_max, srate, crate); + /* FIXME: boundary? */ + slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED; + slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; + err = snd_pcm_params(slave, &slave_params); + params->fail_mask = slave_params.fail_mask; + params->fail_reason = slave_params.fail_reason; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_rate_t *rate = pcm->private; + int src_format, dst_format; + int src_rate, dst_rate; + int mul, div; + int err = snd_pcm_setup(rate->plug.slave, setup); + if (err < 0) + return err; + if (rate->req_sformat >= 0) + assert(rate->req_sformat == setup->format.sfmt); + rate->sformat = setup->format.sfmt; + rate->srate = setup->format.rate; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + src_format = rate->cformat; + dst_format = rate->sformat; + src_rate = rate->crate; + dst_rate = rate->srate; + } else { + src_format = rate->sformat; + dst_format = rate->cformat; + src_rate = rate->srate; + dst_rate = rate->crate; + } + rate->get_idx = getput_index(src_format); + rate->put_idx = getput_index(dst_format); + if (src_rate < dst_rate) { + rate->func = resample_expand; + /* pitch is get_threshold */ + } else { + rate->func = resample_shrink; + /* pitch is get_increment */ + } + rate->pitch = (((u_int64_t)dst_rate * DIV) + src_rate / 2) / src_rate; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + mul = DIV; + div = rate->pitch; + } else { + mul = rate->pitch; + div = DIV; + } + rate->crate = muldiv64(rate->srate, mul, div); + if (rate->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = rate->cxfer_mode; + if (rate->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = rate->cmmap_shape; + setup->format.sfmt = rate->cformat; + setup->format.rate = rate->crate; + /* FIXME */ + setup->rate_master = rate->crate; + setup->rate_divisor = 1; + setup->mmap_bytes = 0; + setup->avail_min = muldiv64(setup->avail_min, mul, div); + setup->xfer_min = muldiv64(setup->xfer_min, mul, div); + setup->xrun_max = muldiv64(setup->xrun_max, mul, div); + + /* FIXME: the three above are not a lot sensible */ + setup->buffer_size = muldiv64(setup->buffer_size, mul, div); + setup->frag_size = muldiv64(setup->frag_size, mul, div); + setup->xfer_align = muldiv64(setup->xfer_align, mul, div); + + /* FIXME */ + setup->boundary = LONG_MAX - LONG_MAX % setup->buffer_size; + + if (rate->states) + free(rate->states); + rate->states = malloc(setup->format.channels * sizeof(*rate->states)); + return 0; +} + +static int snd_pcm_rate_init(snd_pcm_t *pcm) +{ + snd_pcm_rate_t *rate = pcm->private; + unsigned int k; + for (k = 0; k < pcm->setup.format.channels; ++k) { + rate->states[k].sum = 0; + rate->states[k].sample = 0; + if (rate->func == resample_expand) { + /* Get a sample on entry */ + rate->states[k].pos = rate->pitch + DIV; + } else { + rate->states[k].pos = 0; + } + } + return 0; +} + +static ssize_t snd_pcm_rate_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t client_offset, + size_t client_size, + size_t *slave_sizep) +{ + snd_pcm_rate_t *rate = pcm->private; + snd_pcm_t *slave = rate->plug.slave; + size_t client_xfer = 0; + size_t slave_xfer = 0; + ssize_t err = 0; + size_t slave_size; + if (slave_sizep) + slave_size = *slave_sizep; + else + slave_size = INT_MAX; + assert(client_size > 0 && slave_size > 0); + while (client_xfer < client_size && + slave_xfer < slave_size) { + size_t src_frames, dst_frames; + src_frames = client_size - client_xfer; + dst_frames = snd_pcm_mmap_playback_xfer(slave, slave_size - slave_xfer); + src_frames = rate->func(areas, client_offset, src_frames, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + &dst_frames, + pcm->setup.format.channels, + rate->get_idx, rate->put_idx, + rate->pitch, rate->states); + err = snd_pcm_mmap_forward(slave, dst_frames); + if (err < 0) + break; + assert((size_t)err == dst_frames); + client_offset += src_frames; + client_xfer += src_frames; + snd_pcm_mmap_hw_forward(pcm, src_frames); + slave_xfer += dst_frames; + } + if (client_xfer > 0 || slave_xfer > 0) { + if (slave_sizep) + *slave_sizep = slave_xfer; + return client_xfer; + } + return err; +} + +static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t client_offset, + size_t client_size, + size_t *slave_sizep) + +{ + snd_pcm_rate_t *rate = pcm->private; + snd_pcm_t *slave = rate->plug.slave; + size_t client_xfer = 0; + size_t slave_xfer = 0; + ssize_t err = 0; + size_t slave_size; + if (slave_sizep) + slave_size = *slave_sizep; + else + slave_size = INT_MAX; + assert(client_size > 0 && slave_size > 0); + while (client_xfer < client_size && + slave_xfer < slave_size) { + size_t src_frames, dst_frames; + dst_frames = client_size - client_xfer; + src_frames = snd_pcm_mmap_capture_xfer(slave, slave_size - slave_xfer); + src_frames = rate->func(slave->mmap_areas, snd_pcm_mmap_offset(slave), + src_frames, + areas, client_offset, &dst_frames, + pcm->setup.format.channels, + rate->get_idx, rate->put_idx, + rate->pitch, rate->states); + err = snd_pcm_mmap_forward(slave, src_frames); + if (err < 0) + break; + assert((size_t)err == src_frames); + client_offset += dst_frames; + client_xfer += dst_frames; + snd_pcm_mmap_hw_forward(pcm, dst_frames); + slave_xfer += src_frames; + } + if (client_xfer > 0 || slave_xfer > 0) { + if (slave_sizep) + *slave_sizep = slave_xfer; + return client_xfer; + } + return err; +} + +size_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, size_t frames) +{ + snd_pcm_rate_t *rate = pcm->private; + /* Round toward zero */ + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + return (int64_t)frames * DIV / rate->pitch; + else + return (int64_t)frames * rate->pitch / DIV; +} + +static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_rate_t *rate = pcm->private; + if (rate->req_sformat < 0) + fprintf(fp, "Rate conversion PCM (%d)\n", + rate->req_srate); + else + fprintf(fp, "Rate conversion PCM (%d, sformat=%s)\n", + rate->req_srate, + snd_pcm_format_name(rate->req_sformat)); + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(rate->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_rate_ops = { + close: snd_pcm_rate_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_rate_params_info, + params: snd_pcm_rate_params, + setup: snd_pcm_rate_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_plugin_channel_setup, + dump: snd_pcm_rate_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int snd_pcm_rate_open(snd_pcm_t **handlep, int sformat, int srate, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_rate_t *rate; + int err; + assert(handlep && slave); + if (sformat >= 0 && snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + rate = calloc(1, sizeof(snd_pcm_rate_t)); + if (!rate) { + return -ENOMEM; + } + rate->req_srate = srate; + rate->req_sformat = sformat; + rate->plug.read = snd_pcm_rate_read_areas; + rate->plug.write = snd_pcm_rate_write_areas; + rate->plug.client_frames = snd_pcm_rate_client_frames; + rate->plug.init = snd_pcm_rate_init; + rate->plug.slave = slave; + rate->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(rate); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_RATE; + handle->stream = slave->stream; + handle->ops = &snd_pcm_rate_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = rate; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int _snd_pcm_rate_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + long srate = -1; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + continue; + } + if (strcmp(n->id, "srate") == 0) { + err = snd_config_integer_get(n, &srate); + if (err < 0) + return -EINVAL; + continue; + } + return -EINVAL; + } + if (!sname || !srate) + return -EINVAL; + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_rate_open(pcmp, sformat, srate, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c new file mode 100644 index 00000000..406f9f34 --- /dev/null +++ b/src/pcm/pcm_route.c @@ -0,0 +1,963 @@ +/* + * PCM - Linear conversion + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * 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 <byteswap.h> +#include <math.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +/* The best possible hack to support missing optimization in gcc 2.7.2.3 */ +#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 +#define div(a) a /= ROUTE_PLUGIN_RESOLUTION +#elif ROUTE_PLUGIN_RESOLUTION == 16 +#define div(a) a >>= 4 +#else +#error "Add some code here" +#endif + +typedef struct { + int channel; + int as_int; +#if ROUTE_PLUGIN_FLOAT + float as_float; +#endif +} ttable_src_t; + +typedef struct ttable_dst ttable_dst_t; + +typedef struct { + enum {UINT32=0, UINT64=1, FLOAT=2} sum_idx; + int get_idx; + int put_idx; + int conv_idx; + int src_size; + int dst_sfmt; + size_t ndsts; + ttable_dst_t *dsts; +} route_params_t; + + +typedef void (*route_f)(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_area, + size_t dst_offset, + size_t frames, + ttable_dst_t *ttable, + route_params_t *params); + +struct ttable_dst { + int att; /* Attenuated */ + unsigned int nsrcs; + ttable_src_t* srcs; + route_f func; +}; + +typedef union { + u_int32_t as_uint32; + u_int64_t as_uint64; +#if ROUTE_PLUGIN_FLOAT + float as_float; +#endif +} sum_t; + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + int req_sformat, req_schannels; + int sformat; + int cformat; + int schannels; + int cchannels; + int cxfer_mode, cmmap_shape; + route_params_t params; +} snd_pcm_route_t; + + +static void route1_zero(snd_pcm_channel_area_t *src_areas ATTRIBUTE_UNUSED, + size_t src_offset ATTRIBUTE_UNUSED, + snd_pcm_channel_area_t *dst_area, + size_t dst_offset, + size_t frames, + ttable_dst_t* ttable ATTRIBUTE_UNUSED, + route_params_t *params) +{ +#if 0 + if (dst_area->wanted) + snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt); + dsts_area->enabled = 0; +#else + snd_pcm_area_silence(dst_area, dst_offset, frames, params->dst_sfmt); +#endif +} + +static void route1_one(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_area, + size_t dst_offset, + size_t frames, + ttable_dst_t* ttable, + route_params_t *params) +{ +#define CONV_LABELS +#include "plugin_ops.h" +#undef CONV_LABELS + void *conv; + snd_pcm_channel_area_t *src_area = 0; + unsigned int srcidx; + char *src, *dst; + int src_step, dst_step; + for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { + src_area = &src_areas[ttable->srcs[srcidx].channel]; + if (src_area->addr != NULL) + break; + } + if (srcidx == ttable->nsrcs) { + route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params); + return; + } + +#if 0 + dst_area->enabled = 1; +#endif + conv = conv_labels[params->conv_idx]; + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area); + dst_step = snd_pcm_channel_area_step(dst_area); + while (frames-- > 0) { + goto *conv; +#define CONV_END after +#include "plugin_ops.h" +#undef CONV_END + after: + src += src_step; + dst += dst_step; + } +} + +static void route1_many(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_area, + size_t dst_offset, + size_t frames, + ttable_dst_t* ttable, + route_params_t *params) +{ +#define GET_U_LABELS +#define PUT_U32_LABELS +#include "plugin_ops.h" +#undef GET_U_LABELS +#undef PUT_U32_LABELS + static void *zero_labels[3] = { &&zero_int32, &&zero_int64, +#if ROUTE_PLUGIN_FLOAT + &&zero_float +#endif + }; + /* sum_type att */ + static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att, + &&add_int64_noatt, &&add_int64_att, +#if ROUTE_PLUGIN_FLOAT + &&add_float_noatt, &&add_float_att +#endif + }; + /* sum_type att shift */ + static void *norm_labels[3 * 2 * 4] = { 0, + &&norm_int32_8_noatt, + &&norm_int32_16_noatt, + &&norm_int32_24_noatt, + 0, + &&norm_int32_8_att, + &&norm_int32_16_att, + &&norm_int32_24_att, + &&norm_int64_0_noatt, + &&norm_int64_8_noatt, + &&norm_int64_16_noatt, + &&norm_int64_24_noatt, + &&norm_int64_0_att, + &&norm_int64_8_att, + &&norm_int64_16_att, + &&norm_int64_24_att, +#if ROUTE_PLUGIN_FLOAT + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, + &&norm_float_0, + &&norm_float_8, + &&norm_float_16, + &&norm_float_24, +#endif + }; + void *zero, *get, *add, *norm, *put_u32; + int nsrcs = ttable->nsrcs; + char *dst; + int dst_step; + char *srcs[nsrcs]; + int src_steps[nsrcs]; + ttable_src_t src_tt[nsrcs]; + u_int32_t sample = 0; + int srcidx, srcidx1 = 0; + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + snd_pcm_channel_area_t *src_area = &src_areas[ttable->srcs[srcidx].channel]; +#if 0 + if (!src_area->enabled) + continue; +#endif + srcs[srcidx1] = snd_pcm_channel_area_addr(src_area, src_offset); + src_steps[srcidx1] = snd_pcm_channel_area_step(src_area); + src_tt[srcidx1] = ttable->srcs[srcidx]; + srcidx1++; + } + nsrcs = srcidx1; + if (nsrcs == 0) { + route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params); + return; + } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { + route1_one(src_areas, src_offset, dst_area, dst_offset, frames, ttable, params); + return; + } + +#if 0 + dst_area->enabled = 1; +#endif + zero = zero_labels[params->sum_idx]; + get = get_u_labels[params->get_idx]; + add = add_labels[params->sum_idx * 2 + ttable->att]; + norm = norm_labels[params->sum_idx * 8 + ttable->att * 4 + 4 - params->src_size]; + put_u32 = put_u32_labels[params->put_idx]; + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + dst_step = snd_pcm_channel_area_step(dst_area); + + while (frames-- > 0) { + ttable_src_t *ttp = src_tt; + sum_t sum; + + /* Zero sum */ + goto *zero; + zero_int32: + sum.as_uint32 = 0; + goto zero_end; + zero_int64: + sum.as_uint64 = 0; + goto zero_end; +#if ROUTE_PLUGIN_FLOAT + zero_float: + sum.as_float = 0.0; + goto zero_end; +#endif + zero_end: + for (srcidx = 0; srcidx < nsrcs; ++srcidx) { + char *src = srcs[srcidx]; + + /* Get sample */ + goto *get; +#define GET_U_END after_get +#include "plugin_ops.h" +#undef GET_U_END + after_get: + + /* Sum */ + goto *add; + add_int32_att: + sum.as_uint32 += sample * ttp->as_int; + goto after_sum; + add_int32_noatt: + if (ttp->as_int) + sum.as_uint32 += sample; + goto after_sum; + add_int64_att: + sum.as_uint64 += (u_int64_t) sample * ttp->as_int; + goto after_sum; + add_int64_noatt: + if (ttp->as_int) + sum.as_uint64 += sample; + goto after_sum; +#if ROUTE_PLUGIN_FLOAT + add_float_att: + sum.as_float += sample * ttp->as_float; + goto after_sum; + add_float_noatt: + if (ttp->as_int) + sum.as_float += sample; + goto after_sum; +#endif + after_sum: + srcs[srcidx] += src_steps[srcidx]; + ttp++; + } + + /* Normalization */ + goto *norm; + norm_int32_8_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_att: + sum.as_uint64 <<= 8; + norm_int64_0_att: + div(sum.as_uint64); + goto norm_int; + + norm_int32_16_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_att: + sum.as_uint64 <<= 16; + div(sum.as_uint64); + goto norm_int; + + norm_int32_24_att: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_att: + sum.as_uint64 <<= 24; + div(sum.as_uint64); + goto norm_int; + + norm_int32_8_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_8_noatt: + sum.as_uint64 <<= 8; + goto norm_int; + + norm_int32_16_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_16_noatt: + sum.as_uint64 <<= 16; + goto norm_int; + + norm_int32_24_noatt: + sum.as_uint64 = sum.as_uint32; + norm_int64_24_noatt: + sum.as_uint64 <<= 24; + goto norm_int; + + norm_int64_0_noatt: + norm_int: + if (sum.as_uint64 > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_uint64; + goto after_norm; + +#if ROUTE_PLUGIN_FLOAT + norm_float_8: + sum.as_float *= 1 << 8; + goto norm_float; + norm_float_16: + sum.as_float *= 1 << 16; + goto norm_float; + norm_float_24: + sum.as_float *= 1 << 24; + goto norm_float; + norm_float_0: + norm_float: + sum.as_float = floor(sum.as_float + 0.5); + if (sum.as_float > (u_int32_t)0xffffffff) + sample = (u_int32_t)0xffffffff; + else + sample = sum.as_float; + goto after_norm; +#endif + after_norm: + + /* Put sample */ + goto *put_u32; +#define PUT_U32_END after_put_u32 +#include "plugin_ops.h" +#undef PUT_U32_END + after_put_u32: + + dst += dst_step; + } +} + +static void route_transfer(snd_pcm_channel_area_t *src_areas, + size_t src_offset, + snd_pcm_channel_area_t *dst_areas, + size_t dst_offset, + size_t frames, + size_t dst_channels, + route_params_t *params) +{ + size_t dst_channel; + ttable_dst_t *dstp; + snd_pcm_channel_area_t *dst_area; + + dstp = params->dsts; + dst_area = dst_areas; + for (dst_channel = 0; dst_channel < dst_channels; ++dst_channel) { + if (dst_channel >= params->ndsts) + route1_zero(src_areas, src_offset, dst_area, dst_offset, frames, dstp, params); + else + dstp->func(src_areas, src_offset, dst_area, dst_offset, frames, dstp, params); + dstp++; + dst_area++; + } +} + +static int snd_pcm_route_close(snd_pcm_t *pcm) +{ + snd_pcm_route_t *route = pcm->private; + route_params_t *params = &route->params; + int err = 0; + size_t dst_channel; + if (route->plug.close_slave) + err = snd_pcm_close(route->plug.slave); + if (params->dsts) { + for (dst_channel = 0; dst_channel < params->ndsts; ++dst_channel) { + if (params->dsts[dst_channel].srcs != NULL) + free(params->dsts[dst_channel].srcs); + } + free(params->dsts); + } + free(route); + return 0; +} + +static int snd_pcm_route_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info) +{ + snd_pcm_route_t *route = pcm->private; + unsigned int req_mask = info->req_mask; + unsigned int sfmt = info->req.format.sfmt; + unsigned int channels = info->req.format.channels; + int err; + if (req_mask & SND_PCM_PARAMS_SFMT && + !snd_pcm_format_linear(sfmt)) { + info->req.fail_mask = SND_PCM_PARAMS_SFMT; + info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (route->req_sformat >= 0) { + info->req_mask |= SND_PCM_PARAMS_SFMT; + info->req.format.sfmt = route->req_sformat; + } + if (route->req_schannels >= 0) { + info->req_mask |= SND_PCM_PARAMS_CHANNELS; + info->req.format.channels = route->req_schannels; + } + info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | + SND_PCM_PARAMS_XFER_MODE); + err = snd_pcm_params_info(route->plug.slave, info); + info->req_mask = req_mask; + info->req.format.sfmt = sfmt; + info->req.format.channels = channels; + if (err < 0) + return err; + if (req_mask & SND_PCM_PARAMS_SFMT) + info->formats = 1 << sfmt; + else + info->formats = SND_PCM_LINEAR_FORMATS; + if (req_mask & SND_PCM_PARAMS_CHANNELS) { + info->min_channels = channels; + info->max_channels = channels; + } else { + info->min_channels = 1; + info->max_channels = 1024; + } + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED; + return err; +} + +static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params) +{ + snd_pcm_route_t *route = pcm->private; + snd_pcm_t *slave = route->plug.slave; + int err; + if (!snd_pcm_format_linear(params->format.sfmt)) { + params->fail_mask = SND_PCM_PARAMS_SFMT; + params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL; + return -EINVAL; + } + if (slave->mmap_data) { + err = snd_pcm_munmap_data(slave); + if (err < 0) + return err; + } + route->cformat = params->format.sfmt; + route->cchannels = params->format.channels; + route->cxfer_mode = params->xfer_mode; + route->cmmap_shape = params->mmap_shape; + if (route->req_sformat >= 0) + params->format.sfmt = route->req_sformat; + if (route->req_schannels >= 0) + params->format.channels = route->req_schannels; + params->xfer_mode = SND_PCM_XFER_UNSPECIFIED; + params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;; + err = snd_pcm_params(slave, params); + params->format.sfmt = route->cformat; + params->format.channels = route->cchannels; + params->xfer_mode = route->cxfer_mode; + params->mmap_shape = route->cmmap_shape; + if (slave->valid_setup) { + int r = snd_pcm_mmap_data(slave, NULL); + assert(r >= 0); + } + return err; +} + +static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup) +{ + snd_pcm_route_t *route = pcm->private; + int src_format, dst_format; + int err = snd_pcm_setup(route->plug.slave, setup); + if (err < 0) + return err; + if (route->req_sformat >= 0) + assert(route->req_sformat == setup->format.sfmt); + route->sformat = setup->format.sfmt; + route->schannels = setup->format.channels; + if (route->cxfer_mode == SND_PCM_XFER_UNSPECIFIED) + setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; + else + setup->xfer_mode = route->cxfer_mode; + if (route->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED) + setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED; + else + setup->mmap_shape = route->cmmap_shape; + setup->format.sfmt = route->cformat; + setup->format.channels = route->cchannels; + setup->mmap_bytes = 0; + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { + src_format = route->cformat; + dst_format = route->sformat; + } else { + src_format = route->sformat; + dst_format = route->cformat; + } + route->params.get_idx = getput_index(src_format); + route->params.put_idx = getput_index(dst_format); + route->params.conv_idx = conv_index(src_format, dst_format); + route->params.src_size = snd_pcm_format_width(src_format) / 8; + route->params.dst_sfmt = dst_format; +#if ROUTE_PLUGIN_FLOAT + route->params.sum_idx = FLOAT; +#else + if (src_size == 4) + route->params.sum_idx = UINT64; + else + route->params.sum_idx = UINT32; +#endif + return 0; +} + +static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_route_t *route = pcm->private; + snd_pcm_t *slave = route->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + route_transfer(areas, offset, + slave->mmap_areas, snd_pcm_mmap_offset(slave), + frames, route->schannels, &route->params); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static int snd_pcm_route_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup) +{ +#if 0 + snd_pcm_plugin_t *plugin = pcm->private; + int err; + err = snd_pcm_channel_setup(plugin->slave, setup); + if (err < 0) + return err; +#endif + if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) { + setup->area.addr = pcm->mmap_data; + setup->area.first = setup->channel * pcm->bits_per_sample; + setup->area.step = pcm->bits_per_frame; + } else { + setup->area.addr = pcm->mmap_data + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8; + setup->area.first = 0; + setup->area.step = pcm->bits_per_sample; + } + return 0; +} + +static ssize_t snd_pcm_route_read_areas(snd_pcm_t *pcm, + snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_route_t *route = pcm->private; + snd_pcm_t *slave = route->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + route_transfer(slave->mmap_areas, snd_pcm_mmap_offset(slave), + areas, offset, + frames, route->cchannels, &route->params); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_route_t *route = pcm->private; + unsigned int dst; + if (route->req_sformat < 0) + fprintf(fp, "Route conversion PCM\n"); + else + fprintf(fp, "Route conversion PCM (sformat=%s)\n", + snd_pcm_format_name(route->req_sformat)); + fputs("Transformation table:\n", fp); + for (dst = 0; dst < route->params.ndsts; dst++) { + ttable_dst_t *d = &route->params.dsts[dst]; + unsigned int src; + if (d->nsrcs == 0) + continue; + fprintf(fp, "%d <- ", dst); + src = 0; + while (1) { + ttable_src_t *s = &d->srcs[src]; + if (d->att) + fprintf(fp, "%d*%g", s->channel, s->as_float); + else + fprintf(fp, "%d", s->channel); + src++; + if (src == d->nsrcs) + break; + fputs(" + ", fp); + } + putc('\n', fp); + } + if (pcm->valid_setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(route->plug.slave, fp); +} + +struct snd_pcm_ops snd_pcm_route_ops = { + close: snd_pcm_route_close, + info: snd_pcm_plugin_info, + params_info: snd_pcm_route_params_info, + params: snd_pcm_route_params, + setup: snd_pcm_route_setup, + channel_info: snd_pcm_plugin_channel_info, + channel_params: snd_pcm_plugin_channel_params, + channel_setup: snd_pcm_route_channel_setup, + dump: snd_pcm_route_dump, + nonblock: snd_pcm_plugin_nonblock, + mmap_status: snd_pcm_plugin_mmap_status, + mmap_control: snd_pcm_plugin_mmap_control, + mmap_data: snd_pcm_plugin_mmap_data, + munmap_status: snd_pcm_plugin_munmap_status, + munmap_control: snd_pcm_plugin_munmap_control, + munmap_data: snd_pcm_plugin_munmap_data, +}; + +int route_load_ttable(route_params_t *params, int stream, + unsigned int tt_ssize, + ttable_entry_t *ttable, + unsigned int tt_cused, unsigned int tt_sused) +{ + unsigned int src_channel, dst_channel; + ttable_dst_t *dptr; + unsigned int sused, dused, smul, dmul; + if (stream == SND_PCM_STREAM_PLAYBACK) { + sused = tt_cused; + dused = tt_sused; + smul = tt_ssize; + dmul = 1; + } else { + sused = tt_sused; + dused = tt_cused; + smul = 1; + dmul = tt_ssize; + } + params->ndsts = dused; + dptr = calloc(dused, sizeof(*params->dsts)); + if (!dptr) + return -ENOMEM; + params->dsts = dptr; + for (dst_channel = 0; dst_channel < dused; ++dst_channel) { + ttable_entry_t t = 0; + int att = 0; + int nsrcs = 0; + ttable_src_t srcs[sused]; + for (src_channel = 0; src_channel < sused; ++src_channel) { + ttable_entry_t v; + v = ttable[src_channel * smul + dst_channel * dmul]; + assert(v >= 0 && v <= FULL); + if (v != 0) { + srcs[nsrcs].channel = src_channel; +#if ROUTE_PLUGIN_FLOAT + /* Also in user space for non attenuated */ + srcs[nsrcs].as_int = (v == FULL ? ROUTE_PLUGIN_RESOLUTION : 0); + srcs[nsrcs].as_float = v; +#else + srcs[nsrcs].as_int = v; +#endif + if (v != FULL) + att = 1; + t += v; + nsrcs++; + } + } +#if 0 + assert(t <= FULL); +#endif + dptr->att = att; + dptr->nsrcs = nsrcs; + if (nsrcs == 0) + dptr->func = route1_zero; + else if (nsrcs == 1 && !att) + dptr->func = route1_one; + else + dptr->func = route1_many; + if (nsrcs > 0) { + dptr->srcs = calloc(nsrcs, sizeof(*srcs)); + if (!dptr->srcs) + return -ENOMEM; + memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs); + } else + dptr->srcs = 0; + dptr++; + } + return 0; +} + + +int snd_pcm_route_open(snd_pcm_t **handlep, + int sformat, unsigned int schannels, + ttable_entry_t *ttable, + unsigned int tt_ssize, + unsigned int tt_cused, unsigned int tt_sused, + snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *handle; + snd_pcm_route_t *route; + int err; + assert(handlep && slave && ttable); + if (sformat >= 0 && snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + route = calloc(1, sizeof(snd_pcm_route_t)); + if (!route) { + return -ENOMEM; + } + route->req_sformat = sformat; + route->req_schannels = schannels; + route->plug.read = snd_pcm_route_read_areas; + route->plug.write = snd_pcm_route_write_areas; + route->plug.slave = slave; + route->plug.close_slave = close_slave; + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) { + free(route); + return -ENOMEM; + } + handle->type = SND_PCM_TYPE_ROUTE; + handle->stream = slave->stream; + handle->ops = &snd_pcm_route_ops; + handle->op_arg = handle; + handle->fast_ops = &snd_pcm_plugin_fast_ops; + handle->fast_op_arg = handle; + handle->mode = slave->mode; + handle->private = route; + err = snd_pcm_init(handle); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + err = route_load_ttable(&route->params, handle->stream, tt_ssize, ttable, tt_cused, tt_sused); + if (err < 0) { + snd_pcm_close(handle); + return err; + } + *handlep = handle; + + return 0; +} + +int snd_pcm_route_load_ttable(snd_config_t *tt, ttable_entry_t *ttable, + unsigned int tt_csize, unsigned int tt_ssize, + unsigned int *tt_cused, unsigned int *tt_sused, + int schannels) +{ + int cused = -1; + int sused = -1; + snd_config_iterator_t i; + unsigned int k; + for (k = 0; k < tt_csize * tt_ssize; ++k) + ttable[k] = 0.0; + snd_config_foreach(i, tt) { + snd_config_t *in = snd_config_entry(i); + snd_config_iterator_t j; + char *p; + long cchannel; + errno = 0; + cchannel = strtol(in->id, &p, 10); + if (errno || *p || + cchannel < 0 || (unsigned int) cchannel > tt_csize) + return -EINVAL; + if (snd_config_type(in) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + snd_config_foreach(j, in) { + snd_config_t *jn = snd_config_entry(j); + double value; + long schannel; + int err; + errno = 0; + schannel = strtol(jn->id, &p, 10); + if (errno || *p || + schannel < 0 || (unsigned int) schannel > tt_ssize || + (schannels > 0 && schannel >= schannels)) + return -EINVAL; + err = snd_config_real_get(jn, &value); + if (err < 0) { + long v; + err = snd_config_integer_get(jn, &v); + if (err < 0) + return -EINVAL; + value = v; + } + ttable[cchannel * tt_ssize + schannel] = value; + if (schannel > sused) + sused = schannel; + } + if (cchannel > cused) + cused = cchannel; + } + *tt_sused = sused + 1; + *tt_cused = cused + 1; + return 0; +} + +#define MAX_CHANNELS 32 + +int _snd_pcm_route_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + int sformat = -1; + long schannels = -1; + snd_config_t *tt = NULL; + ttable_entry_t ttable[MAX_CHANNELS*MAX_CHANNELS]; + unsigned int cused, sused; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "sformat") == 0) { + char *f; + err = snd_config_string_get(n, &f); + if (err < 0) + return -EINVAL; + sformat = snd_pcm_format_value(f); + if (sformat < 0) + return -EINVAL; + if (snd_pcm_format_linear(sformat) != 1) + return -EINVAL; + continue; + } + if (strcmp(n->id, "schannels") == 0) { + err = snd_config_integer_get(n, &schannels); + if (err < 0) + return -EINVAL; + continue; + } + if (strcmp(n->id, "ttable") == 0) { + if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) + return -EINVAL; + tt = n; + continue; + } + return -EINVAL; + } + if (!sname || !tt) + return -EINVAL; + + err = snd_pcm_route_load_ttable(tt, ttable, MAX_CHANNELS, MAX_CHANNELS, + &cused, &sused, schannels); + if (err < 0) + return err; + + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_route_open(pcmp, sformat, schannels, + ttable, MAX_CHANNELS, + cused, sused, + spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + + diff --git a/src/pcm/plugin/Makefile.am b/src/pcm/plugin/Makefile.am deleted file mode 100644 index 5d79297e..00000000 --- a/src/pcm/plugin/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -EXTRA_LTLIBRARIES = libpcmplugin.la - -libpcmplugin_la_SOURCES = io.c mmap.c copy.c linear.c \ - mulaw.c alaw.c adpcm.c rate.c route.c -all: libpcmplugin.la - - -INCLUDES=-I$(top_srcdir)/include diff --git a/src/pcm/plugin/adpcm.c b/src/pcm/plugin/adpcm.c deleted file mode 100644 index efac226b..00000000 --- a/src/pcm/plugin/adpcm.c +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Ima-ADPCM conversion Plug-In Interface - * Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si> - * Jaroslav Kysela <perex@suse.cz> - * - * Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code - * by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992 - * by Stichting Mathematisch Centrum, Amsterdam, The Netherlands. - * - * 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. - * - */ - -/* -These routines convert 16 bit linear PCM samples to 4 bit ADPCM code -and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which -is being recommended by the IMA Digital Audio Technical Working Group. - -The algorithm for this coder was taken from: -Proposal for Standardized Audio Interstreamge Formats, -IMA compatability project proceedings, Vol 2, Issue 2, May 1992. - -- No, this is *not* a G.721 coder/decoder. The algorithm used by G.721 - is very complicated, requiring oodles of floating-point ops per - sample (resulting in very poor performance). I have not done any - tests myself but various people have assured my that 721 quality is - actually lower than DVI quality. - -- No, it probably isn't a RIFF ADPCM decoder either. Trying to decode - RIFF ADPCM with these routines seems to result in something - recognizable but very distorted. - -- No, it is not a CDROM-XA coder either, as far as I know. I haven't - come across a good description of XA yet. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include "../pcm_local.h" - -/* First table lookup for Ima-ADPCM quantizer */ -static char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 }; - -/* Second table lookup for Ima-ADPCM quantizer */ -static short StepSize[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, - 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, - 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, - 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 -}; - -typedef struct { - int pred_val; /* Calculated predicted value */ - int step_idx; /* Previous StepSize lookup index */ -} adpcm_channel_t; - -typedef void (*adpcm_f)(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames); - -typedef struct adpcm_private_data { - adpcm_f func; - int conv; - adpcm_channel_t channels[0]; -} adpcm_t; - - -static void adpcm_init(snd_pcm_plugin_t *plugin) -{ - unsigned int channel; - adpcm_t *data = (adpcm_t *)plugin->extra_data; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - adpcm_channel_t *v = &data->channels[channel]; - v->pred_val = 0; - v->step_idx = 0; - } -} - -static char adpcm_encoder(int sl, adpcm_channel_t * state) -{ - short diff; /* Difference between sl and predicted sample */ - short pred_diff; /* Predicted difference to next sample */ - - unsigned char sign; /* sign of diff */ - short step; /* holds previous StepSize value */ - unsigned char adjust_idx; /* Index to IndexAdjust lookup table */ - - int i; - - /* Compute difference to previous predicted value */ - diff = sl - state->pred_val; - sign = (diff < 0) ? 0x8 : 0x0; - if (sign) { - diff = -diff; - } - - /* - * This code *approximately* computes: - * adjust_idx = diff * 4 / step; - * pred_diff = (adjust_idx + 0.5) * step / 4; - * - * But in shift step bits are dropped. The net result of this is - * that even if you have fast mul/div hardware you cannot put it to - * good use since the fixup would be too expensive. - */ - - step = StepSize[state->step_idx]; - - /* Divide and clamp */ - pred_diff = step >> 3; - for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) { - if (diff >= step) { - adjust_idx |= i; - diff -= step; - pred_diff += step; - } - } - - /* Update and clamp previous predicted value */ - state->pred_val += sign ? -pred_diff : pred_diff; - - if (state->pred_val > 32767) { - state->pred_val = 32767; - } else if (state->pred_val < -32768) { - state->pred_val = -32768; - } - - /* Update and clamp StepSize lookup table index */ - state->step_idx += IndexAdjust[adjust_idx]; - - if (state->step_idx < 0) { - state->step_idx = 0; - } else if (state->step_idx > 88) { - state->step_idx = 88; - } - return (sign | adjust_idx); -} - - -static int adpcm_decoder(unsigned char code, adpcm_channel_t * state) -{ - short pred_diff; /* Predicted difference to next sample */ - short step; /* holds previous StepSize value */ - char sign; - - int i; - - /* Separate sign and magnitude */ - sign = code & 0x8; - code &= 0x7; - - /* - * Computes pred_diff = (code + 0.5) * step / 4, - * but see comment in adpcm_coder. - */ - - step = StepSize[state->step_idx]; - - /* Compute difference and new predicted value */ - pred_diff = step >> 3; - for (i = 0x4; i; i >>= 1, step >>= 1) { - if (code & i) { - pred_diff += step; - } - } - state->pred_val += (sign) ? -pred_diff : pred_diff; - - /* Clamp output value */ - if (state->pred_val > 32767) { - state->pred_val = 32767; - } else if (state->pred_val < -32768) { - state->pred_val = -32768; - } - - /* Find new StepSize index value */ - state->step_idx += IndexAdjust[code]; - - if (state->step_idx < 0) { - state->step_idx = 0; - } else if (state->step_idx > 88) { - state->step_idx = 88; - } - return (state->pred_val); -} - -/* - * Basic Ima-ADPCM plugin - */ - -static void adpcm_decode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef PUT_S16_LABELS - adpcm_t *data = (adpcm_t *)plugin->extra_data; - void *put = put_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - int srcbit; - char *dst; - int src_step, srcbit_step, dst_step; - size_t frames1; - adpcm_channel_t *state; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - srcbit = src_channels[channel].area.first % 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - srcbit_step = src_channels[channel].area.step % 8; - dst_step = dst_channels[channel].area.step / 8; - state = &data->channels[channel]; - frames1 = frames; - while (frames1-- > 0) { - signed short sample; - int v; - if (srcbit) - v = *src & 0x0f; - else - v = (*src >> 4) & 0x0f; - sample = adpcm_decoder(v, state); - goto *put; -#define PUT_S16_END after -#include "plugin_ops.h" -#undef PUT_S16_END - after: - src += src_step; - srcbit += srcbit_step; - if (srcbit == 8) { - src++; - srcbit = 0; - } - dst += dst_step; - } - } -} - -static void adpcm_encode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define GET_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS - adpcm_t *data = (adpcm_t *)plugin->extra_data; - void *get = get_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - signed short sample = 0; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int dstbit; - int src_step, dst_step, dstbit_step; - size_t frames1; - adpcm_channel_t *state; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - dstbit = dst_channels[channel].area.first % 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - dstbit_step = dst_channels[channel].area.step % 8; - state = &data->channels[channel]; - frames1 = frames; - while (frames1-- > 0) { - int v; - goto *get; -#define GET_S16_END after -#include "plugin_ops.h" -#undef GET_S16_END - after: - v = adpcm_encoder(sample, state); - if (dstbit) - *dst = (*dst & 0xf0) | v; - else - *dst = (*dst & 0x0f) | (v << 4); - src += src_step; - dst += dst_step; - dstbit += dstbit_step; - if (dstbit == 8) { - dst++; - dstbit = 0; - } - } - } -} - -static ssize_t adpcm_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - adpcm_t *data; - unsigned int channel; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - if (plugin->src_format.format == SND_PCM_SFMT_IMA_ADPCM) { - assert(src_channels[channel].area.first % 4 == 0 && - src_channels[channel].area.step % 4 == 0 && - dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0); - } else { - assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0 && - dst_channels[channel].area.first % 4 == 0 && - dst_channels[channel].area.step % 4 == 0); - } - } - data = (adpcm_t *)plugin->extra_data; - data->func(plugin, src_channels, dst_channels, frames); - return frames; -} - -static int adpcm_action(snd_pcm_plugin_t * plugin, - snd_pcm_plugin_action_t action, - unsigned long udata ATTRIBUTE_UNUSED) -{ - assert(plugin); - switch (action) { - case INIT: - case PREPARE: - case DRAIN: - case FLUSH: - adpcm_init(plugin); - break; - default: - break; - } - return 0; /* silenty ignore other actions */ -} - -int snd_pcm_plugin_build_adpcm(snd_pcm_plug_t *plug, - 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; - adpcm_f func; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->rate == dst_format->rate); - assert(src_format->channels == dst_format->channels); - - if (dst_format->format == SND_PCM_SFMT_IMA_ADPCM) { - format = src_format; - func = adpcm_encode; - } - else if (src_format->format == SND_PCM_SFMT_IMA_ADPCM) { - format = dst_format; - func = adpcm_decode; - } - else - assert(0); - assert(snd_pcm_format_linear(format->format)); - - err = snd_pcm_plugin_build(plug, "Ima-ADPCM<->linear conversion", - src_format, dst_format, - sizeof(adpcm_t) + src_format->channels * sizeof(adpcm_channel_t), - &plugin); - if (err < 0) - return err; - data = (adpcm_t *)plugin->extra_data; - data->func = func; - data->conv = getput_index(format->format); - plugin->transfer = adpcm_transfer; - plugin->action = adpcm_action; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/alaw.c b/src/pcm/plugin/alaw.c deleted file mode 100644 index b563b01b..00000000 --- a/src/pcm/plugin/alaw.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - * A-Law conversion Plug-In Interface - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * Uros Bizjak <uros@kss-loka.si> - * - * Based on reference implementation by Sun Microsystems, Inc. - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <sys/uio.h> -#include "../pcm_local.h" - -#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */ -#define QUANT_MASK (0xf) /* Quantization field mask. */ -#define NSEGS (8) /* Number of A-law segments. */ -#define SEG_SHIFT (4) /* Left shift for segment number. */ -#define SEG_MASK (0x70) /* Segment field mask. */ - -static short alaw_seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, - 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; - -static inline int search(int val, short *table, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (val <= *table++) - return (i); - } - return (size); -} - -/* - * linear2alaw() - Convert a 16-bit linear PCM value to 8-bit A-law - * - * linear2alaw() accepts an 16-bit integer and encodes it as A-law data. - * - * Linear Input Code Compressed Code - * ------------------------ --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static unsigned char linear2alaw(int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char aval; - - if (pcm_val >= 0) { - mask = 0xD5; /* sign (7th) bit = 1 */ - } else { - mask = 0x55; /* sign bit = 0 */ - pcm_val = -pcm_val - 8; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, alaw_seg_end, NSEGS); - - /* Combine the sign, segment, and quantization bits. */ - - if (seg >= 8) /* out of range, return maximum value. */ - return (0x7F ^ mask); - else { - aval = seg << SEG_SHIFT; - if (seg < 2) - aval |= (pcm_val >> 4) & QUANT_MASK; - else - aval |= (pcm_val >> (seg + 3)) & QUANT_MASK; - return (aval ^ mask); - } -} - -/* - * alaw2linear() - Convert an A-law value to 16-bit linear PCM - * - */ -static int alaw2linear(unsigned char a_val) -{ - int t; - int seg; - - a_val ^= 0x55; - - t = (a_val & QUANT_MASK) << 4; - seg = ((unsigned)a_val & SEG_MASK) >> SEG_SHIFT; - switch (seg) { - case 0: - t += 8; - break; - case 1: - t += 0x108; - break; - default: - t += 0x108; - t <<= seg - 1; - } - return ((a_val & SIGN_BIT) ? t : -t); -} - - -/* - * Basic A-Law plugin - */ - -typedef void (*alaw_f)(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames); - -typedef struct alaw_private_data { - alaw_f func; - int conv; -} alaw_t; - -static void alaw_decode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef PUT_S16_LABELS - alaw_t *data = (alaw_t *)plugin->extra_data; - void *put = put_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int src_step, dst_step; - size_t frames1; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - frames1 = frames; - while (frames1-- > 0) { - signed short sample = alaw2linear(*src); - goto *put; -#define PUT_S16_END after -#include "plugin_ops.h" -#undef PUT_S16_END - after: - src += src_step; - dst += dst_step; - } - } -} - -static void alaw_encode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define GET_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS - alaw_t *data = (alaw_t *)plugin->extra_data; - void *get = get_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - signed short sample = 0; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int src_step, dst_step; - size_t frames1; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - frames1 = frames; - while (frames1-- > 0) { - goto *get; -#define GET_S16_END after -#include "plugin_ops.h" -#undef GET_S16_END - after: - *dst = linear2alaw(sample); - src += src_step; - dst += dst_step; - } - } -} - -static ssize_t alaw_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - alaw_t *data; - unsigned int channel; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0); - assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0); - } - data = (alaw_t *)plugin->extra_data; - data->func(plugin, src_channels, dst_channels, frames); - return frames; -} - -int snd_pcm_plugin_build_alaw(snd_pcm_plug_t *plug, - 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; - alaw_f func; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->rate == dst_format->rate); - assert(src_format->channels == dst_format->channels); - - if (dst_format->format == SND_PCM_SFMT_A_LAW) { - format = src_format; - func = alaw_encode; - } - else if (src_format->format == SND_PCM_SFMT_A_LAW) { - format = dst_format; - func = alaw_decode; - } - else - assert(0); - assert(snd_pcm_format_linear(format->format)); - - err = snd_pcm_plugin_build(plug, "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); - plugin->transfer = alaw_transfer; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/copy.c b/src/pcm/plugin/copy.c deleted file mode 100644 index de4762dd..00000000 --- a/src/pcm/plugin/copy.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Linear conversion Plug-In - * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> - * - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <sys/uio.h> -#include "../pcm_local.h" -#endif - -static ssize_t copy_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - unsigned int channel; - unsigned int nchannels; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; channel++) { - assert(src_channels->area.first % 8 == 0 && - src_channels->area.step % 8 == 0); - assert(dst_channels->area.first % 8 == 0 && - dst_channels->area.step % 8 == 0); - if (!src_channels->enabled) { - if (dst_channels->wanted) - snd_pcm_area_silence(&dst_channels->area, 0, frames, plugin->dst_format.format); - dst_channels->enabled = 0; - continue; - } - dst_channels->enabled = 1; - snd_pcm_area_copy(&src_channels->area, 0, &dst_channels->area, 0, frames, plugin->src_format.format); - src_channels++; - dst_channels++; - } - return frames; -} - -int snd_pcm_plugin_build_copy(snd_pcm_plug_t *plug, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - snd_pcm_plugin_t **r_plugin) -{ - int err; - snd_pcm_plugin_t *plugin; - int width; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->format == dst_format->format); - assert(src_format->rate == dst_format->rate); - assert(src_format->channels == dst_format->channels); - - width = snd_pcm_format_physical_width(src_format->format); - assert(width > 0); - - err = snd_pcm_plugin_build(plug, "copy", src_format, dst_format, - 0, &plugin); - if (err < 0) - return err; - plugin->transfer = copy_transfer; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/io.c b/src/pcm/plugin/io.c deleted file mode 100644 index 6ef33cdd..00000000 --- a/src/pcm/plugin/io.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * PCM I/O Plug-In Interface - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * - * - * 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" -#define pcm_write(plug,buf,count) snd_pcm_oss_write3(plug,buf,count,1) -#define pcm_writev(plug,vec,count) snd_pcm_oss_writev3(plug,vec,count,1) -#define pcm_read(plug,buf,count) snd_pcm_oss_read3(plug,buf,count,1) -#define pcm_readv(plug,vec,count) snd_pcm_oss_readv3(plug,vec,count,1) -#else -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <sys/uio.h> -#include "../pcm_local.h" -#define pcm_write(plug,buf,count) snd_pcm_write(plug->slave,buf,count) -#define pcm_writev(plug,vec,count) snd_pcm_writev(plug->slave,vec,count) -#define pcm_read(plug,buf,count) snd_pcm_read(plug->slave,buf,count) -#define pcm_readv(plug,vec,count) snd_pcm_readv(plug->slave,vec,count) -#endif - -/* - * Basic io plugin - */ - -static ssize_t io_playback_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, - size_t frames) -{ - struct iovec *vec; - int count, channel; - - assert(plugin); - vec = (struct iovec *)plugin->extra_data; - assert(vec); - assert(src_channels); - count = plugin->src_format.channels; - if (plugin->src_format.interleave) { - return pcm_write(plugin->plug, src_channels->area.addr, frames); - } else { - for (channel = 0; channel < count; channel++) { - if (src_channels[channel].enabled) - vec[channel].iov_base = src_channels[channel].area.addr; - else - vec[channel].iov_base = 0; - vec[channel].iov_len = frames; - } - return pcm_writev(plugin->plug, vec, count); - } -} - -static ssize_t io_capture_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - struct iovec *vec; - int count, channel; - - assert(plugin); - vec = (struct iovec *)plugin->extra_data; - assert(vec); - assert(dst_channels); - count = plugin->dst_format.channels; - if (plugin->dst_format.interleave) { - return pcm_read(plugin->plug, dst_channels->area.addr, frames); - } else { - for (channel = 0; channel < count; channel++) { - if (dst_channels[channel].enabled) - vec[channel].iov_base = dst_channels[channel].area.addr; - else - vec[channel].iov_base = 0; - vec[channel].iov_len = frames; - } - return pcm_readv(plugin->plug, vec, count); - } - return 0; -} - -static ssize_t io_src_channels(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels) -{ - int err; - unsigned int channel; - snd_pcm_plugin_channel_t *v; - err = snd_pcm_plugin_client_channels(plugin, frames, &v); - if (err < 0) - return err; - *channels = v; - if (plugin->src_format.interleave) { - for (channel = 0; channel < plugin->src_format.channels; ++channel, ++v) - v->wanted = 1; - } - return frames; -} - -#ifndef __KERNEL__ -static void io_dump(snd_pcm_plugin_t *plugin, FILE *fp) -{ - snd_pcm_t *slave = plugin->plug->slave; - if (slave->valid_setup) { - fprintf(fp, "Slave: "); - snd_pcm_dump(slave, fp); - } -} -#endif - -int snd_pcm_plugin_build_io(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_plugin_t **r_plugin) -{ - int err; - snd_pcm_plugin_t *plugin; - - assert(r_plugin); - *r_plugin = NULL; - assert(plug && format); - err = snd_pcm_plugin_build(plug, "I/O io", - format, format, - sizeof(struct iovec) * format->channels, - &plugin); - if (err < 0) - return err; - if (snd_pcm_plug_stream(plug) == SND_PCM_STREAM_PLAYBACK) { - plugin->transfer = io_playback_transfer; - if (format->interleave) - plugin->client_channels = io_src_channels; - } else { - plugin->transfer = io_capture_transfer; - } -#ifndef __KERNEL__ - plugin->dump = io_dump; -#endif - - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/linear.c b/src/pcm/plugin/linear.c deleted file mode 100644 index 83b5e9b7..00000000 --- a/src/pcm/plugin/linear.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Linear conversion Plug-In - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>, - * Abramo Bagnara <abramo@alsa-project.org> - * - * - * This library is free software; you can redistribute it and/or modify - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <sys/uio.h> -#include "../pcm_local.h" -#endif - -/* - * Basic linear conversion plugin - */ - -typedef struct linear_private_data { - int conv; -} linear_t; - -static void convert(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define CONV_LABELS -#include "plugin_ops.h" -#undef CONV_LABELS - linear_t *data = (linear_t *)plugin->extra_data; - void *conv = conv_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int src_step, dst_step; - size_t frames1; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - frames1 = frames; - while (frames1-- > 0) { - goto *conv; -#define CONV_END after -#include "plugin_ops.h" -#undef CONV_END - after: - src += src_step; - dst += dst_step; - } - } -} - -static ssize_t linear_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - linear_t *data; - unsigned int channel; - - assert(plugin && src_channels && dst_channels); - data = (linear_t *)plugin->extra_data; - if (frames == 0) - return 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0); - assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0); - } - convert(plugin, src_channels, dst_channels, frames); - return frames; -} - -int conv_index(int src_format, int dst_format) -{ - int src_endian, dst_endian, sign, src_width, dst_width; - - sign = (snd_pcm_format_signed(src_format) != - snd_pcm_format_signed(dst_format)); -#ifdef SND_LITTLE_ENDIAN - src_endian = snd_pcm_format_big_endian(src_format); - dst_endian = snd_pcm_format_big_endian(dst_format); -#else - src_endian = snd_pcm_format_little_endian(src_format); - dst_endian = snd_pcm_format_little_endian(dst_format); -#endif - - if (src_endian < 0) - src_endian = 0; - if (dst_endian < 0) - dst_endian = 0; - - src_width = snd_pcm_format_width(src_format) / 8 - 1; - dst_width = snd_pcm_format_width(dst_format) / 8 - 1; - - return src_width * 32 + src_endian * 16 + sign * 8 + dst_width * 2 + dst_endian; -} - -int snd_pcm_plugin_build_linear(snd_pcm_plug_t *plug, - 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; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->rate == dst_format->rate); - assert(src_format->channels == dst_format->channels); - assert(snd_pcm_format_linear(src_format->format) && - snd_pcm_format_linear(dst_format->format)); - - err = snd_pcm_plugin_build(plug, "linear format conversion", - src_format, dst_format, - sizeof(linear_t), &plugin); - if (err < 0) - return err; - data = (linear_t *)plugin->extra_data; - 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 deleted file mode 100644 index db1f6a49..00000000 --- a/src/pcm/plugin/mmap.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * PCM MMAP Plug-In Interface - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <sys/poll.h> -#include <sys/uio.h> -#include "../pcm_local.h" - -/* - * Basic mmap plugin - */ - -typedef struct mmap_private_data { - void *buffer; -#if 0 - char *silence; -#endif -} mmap_t; - - -static ssize_t mmap_src_channels(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels) -{ - mmap_t *data; - snd_pcm_plugin_channel_t *sv; - snd_pcm_channel_area_t *dv; - snd_pcm_t *stream; - snd_pcm_setup_t *setup; - size_t pos; - int ready; - unsigned int channel; - - assert(plugin && channels); - data = (mmap_t *)plugin->extra_data; - stream = plugin->plug->slave; - - setup = &stream->setup; - if (snd_pcm_mmap_state(stream) < SND_PCM_STATE_PREPARED) - return -EBADFD; - - ready = snd_pcm_mmap_ready(stream); - if (ready < 0) - return ready; - if (!ready) { - struct pollfd pfd; - if (snd_pcm_mmap_state(stream) != SND_PCM_STATE_RUNNING) - return -EPIPE; - if (stream->mode & SND_PCM_NONBLOCK) - return -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(stream); - pfd.events = POLLOUT | POLLERR; - ready = poll(&pfd, 1, 10000); - if (ready < 0) - return ready; - if (ready == 0 || (pfd.revents & POLLERR)) - return -EPIPE; - assert(snd_pcm_mmap_ready(stream)); - } - pos = snd_pcm_mmap_offset(stream); - assert(pos % setup->align == 0); - - sv = plugin->buf_channels; - dv = stream->channels; - *channels = sv; - for (channel = 0; channel < plugin->src_format.channels; ++channel) { - sv->enabled = 1; -#if 0 - sv->wanted = !data->silence[channel * setup->frags + f]; -#else - sv->wanted = 1; -#endif - sv->area.addr = dv->addr + dv->step * pos / 8; - sv->area.first = dv->first; - sv->area.step = dv->step; - ++sv; - ++dv; - } - return snd_pcm_mmap_xfer(stream, frames); -} - -static ssize_t mmap_dst_channels(snd_pcm_plugin_t *plugin, - size_t frames, - snd_pcm_plugin_channel_t **channels) -{ - mmap_t *data; - int err; - unsigned int channel; - snd_pcm_plugin_channel_t *dv; - snd_pcm_channel_area_t *sv; - snd_pcm_t *stream; - snd_pcm_setup_t *setup; - size_t pos; - int ready; - - assert(plugin && channels); - data = (mmap_t *)plugin->extra_data; - stream = plugin->plug->slave; - - setup = &stream->setup; - if (snd_pcm_mmap_state(stream) < SND_PCM_STATE_PREPARED) - return -EBADFD; - if (snd_pcm_mmap_state(stream) == SND_PCM_STATE_PREPARED && - stream->setup.start_mode == SND_PCM_START_DATA) { - err = snd_pcm_go(stream); - if (err < 0) - return err; - } - ready = snd_pcm_mmap_ready(stream); - if (ready < 0) - return ready; - if (!ready) { - struct pollfd pfd; - if (snd_pcm_mmap_state(stream) != SND_PCM_STATE_RUNNING) - return -EPIPE; - if (stream->mode & SND_PCM_NONBLOCK) - return -EAGAIN; - pfd.fd = snd_pcm_file_descriptor(stream); - pfd.events = POLLIN | POLLERR; - ready = poll(&pfd, 1, 10000); - if (ready < 0) - return ready; - if (ready == 0 || (pfd.revents & POLLERR)) - return -EPIPE; - assert(snd_pcm_mmap_ready(stream)); - } - pos = snd_pcm_mmap_offset(stream); - assert(pos % setup->align == 0); - - sv = stream->channels; - dv = plugin->buf_channels; - *channels = dv; - for (channel = 0; channel < plugin->dst_format.channels; ++channel) { - dv->enabled = 1; - dv->wanted = 0; - dv->area.addr = sv->addr + sv->step * pos / 8; - dv->area.first = sv->first; - dv->area.step = sv->step; - ++sv; - ++dv; - } - return snd_pcm_mmap_xfer(stream, frames); -} - -static ssize_t mmap_playback_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, - size_t frames) -{ - mmap_t *data; - snd_pcm_setup_t *setup; - snd_pcm_t *stream; - int err; - - assert(plugin && plugin->prev); - assert(src_channels); - data = (mmap_t *)plugin->extra_data; - stream = plugin->plug->slave; - setup = &stream->setup; - -#if 0 - for (channel = 0; channel < plugin->src_format.channels; channel++) { - if (src_channels[channel].enabled) - data->silence[channel * setup->frags + f] = 0; - } -#endif - - err = snd_pcm_appl_ptr(stream, frames); - if (err < 0) - return err; - if (snd_pcm_mmap_state(stream) == SND_PCM_STATE_PREPARED && - (setup->start_mode == SND_PCM_START_DATA || - (setup->start_mode == SND_PCM_START_FULL && - !snd_pcm_mmap_ready(stream)))) { - err = snd_pcm_go(stream); - if (err < 0) - return err; - } - return frames; -} - -static ssize_t mmap_capture_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, - snd_pcm_plugin_channel_t *dst_channels ATTRIBUTE_UNUSED, - size_t frames) -{ - mmap_t *data; - snd_pcm_t *stream; - int err; - - assert(plugin && plugin->next); - data = (mmap_t *)plugin->extra_data; - stream = plugin->plug->slave; - - /* FIXME: not here the increment */ - err = snd_pcm_appl_ptr(stream, frames); - if (err < 0) - return err; - - return frames; -} - -static int mmap_action(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_action_t action, - unsigned long udata ATTRIBUTE_UNUSED) -{ - struct mmap_private_data *data; - snd_pcm_t *stream; - - assert(plugin); - stream = plugin->plug->slave; - data = (mmap_t *)plugin->extra_data; - if (action == INIT) { - snd_pcm_setup_t *setup; - int result; - - if (data->buffer) { - snd_pcm_munmap(stream); - data->buffer = 0; - } - result = snd_pcm_mmap(stream, NULL, NULL, (void **)&data->buffer); - if (result < 0) - return result; - setup = &stream->setup; - -#if 0 - if (plugin->stream == SND_PCM_STREAM_PLAYBACK) { - data->silence = malloc(setup->frags * setup->format.channels); - memset(data->silence, 0, setup->frags * setup->format.channels); - } else - data->silence = 0; -#endif - return 0; - } - return 0; /* silenty ignore other actions */ -} - -static void mmap_free(snd_pcm_plugin_t *plugin) -{ - struct mmap_private_data *data; - - if (plugin == NULL) - return; - data = (mmap_t *)plugin->extra_data; -#if 0 - if (data->silence) - free(data->silence); -#endif - if (data->buffer) - snd_pcm_munmap(plugin->plug->slave); -} - -static void mmap_dump(snd_pcm_plugin_t *plugin, FILE *fp) -{ - snd_pcm_t *slave = plugin->plug->slave; - if (slave->valid_setup) { - fprintf(fp, "Slave: "); - snd_pcm_dump(slave, fp); - } -} - -int snd_pcm_plugin_build_mmap(snd_pcm_plug_t *plug, - snd_pcm_format_t *format, - snd_pcm_plugin_t **r_plugin) -{ - int err; - mmap_t *data; - snd_pcm_plugin_t *plugin; - - assert(r_plugin); - *r_plugin = NULL; - assert(plug); - err = snd_pcm_plugin_build(plug, "I/O mmap", - format, format, - sizeof(mmap_t) + sizeof(snd_pcm_plugin_channel_t) * format->channels, - &plugin); - if (err < 0) - return err; - data = (mmap_t *)plugin->extra_data; - if (plug->handle->stream == SND_PCM_STREAM_PLAYBACK) { - plugin->client_channels = mmap_src_channels; - plugin->transfer = mmap_playback_transfer; - } else { - plugin->client_channels = mmap_dst_channels; - plugin->transfer = mmap_capture_transfer; - } - plugin->action = mmap_action; - plugin->private_free = mmap_free; - plugin->dump = mmap_dump; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/mulaw.c b/src/pcm/plugin/mulaw.c deleted file mode 100644 index 74beeb30..00000000 --- a/src/pcm/plugin/mulaw.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Mu-Law conversion Plug-In Interface - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * Uros Bizjak <uros@kss-loka.si> - * - * Based on reference implementation by Sun Microsystems, Inc. - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <sys/uio.h> -#include "../pcm_local.h" -#endif - -#define SIGN_BIT (0x80) /* Sign bit for a u-law byte. */ -#define QUANT_MASK (0xf) /* Quantization field mask. */ -#define NSEGS (8) /* Number of u-law segments. */ -#define SEG_SHIFT (4) /* Left shift for segment number. */ -#define SEG_MASK (0x70) /* Segment field mask. */ - -static short ulaw_seg_end[8] = {0xFF, 0x1FF, 0x3FF, 0x7FF, - 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF}; - -static inline int search(int val, short *table, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (val <= *table++) - return (i); - } - return size; -} - -#define BIAS (0x84) /* Bias for linear code. */ - -/* - * linear2ulaw() - Convert a linear PCM value to u-law - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ -static unsigned char linear2ulaw(int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char uval; - - /* Get the sign and the magnitude of the value. */ - if (pcm_val < 0) { - pcm_val = BIAS - pcm_val; - mask = 0x7F; - } else { - pcm_val += BIAS; - mask = 0xFF; - } - - /* Convert the scaled magnitude to segment number. */ - seg = search(pcm_val, ulaw_seg_end, NSEGS); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - if (seg >= 8) /* out of range, return maximum value. */ - return 0x7F ^ mask; - else { - uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0xF); - return uval ^ mask; - } -} - -/* - * ulaw2linear() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -static int ulaw2linear(unsigned char u_val) -{ - int t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & QUANT_MASK) << 3) + BIAS; - t <<= ((unsigned)u_val & SEG_MASK) >> SEG_SHIFT; - - return ((u_val & SIGN_BIT) ? (BIAS - t) : (t - BIAS)); -} - -/* - * Basic Mu-Law plugin - */ - -typedef void (*mulaw_f)(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames); - -typedef struct mulaw_private_data { - mulaw_f func; - int conv; -} mulaw_t; - -static void mulaw_decode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef PUT_S16_LABELS - mulaw_t *data = (mulaw_t *)plugin->extra_data; - void *put = put_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int src_step, dst_step; - size_t frames1; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - frames1 = frames; - while (frames1-- > 0) { - signed short sample = ulaw2linear(*src); - goto *put; -#define PUT_S16_END after -#include "plugin_ops.h" -#undef PUT_S16_END - after: - src += src_step; - dst += dst_step; - } - } -} - -static void mulaw_encode(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ -#define GET_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS - mulaw_t *data = (mulaw_t *)plugin->extra_data; - void *get = get_s16_labels[data->conv]; - int channel; - int nchannels = plugin->src_format.channels; - signed short sample = 0; - for (channel = 0; channel < nchannels; ++channel) { - char *src; - char *dst; - int src_step, dst_step; - size_t frames1; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - frames1 = frames; - while (frames1-- > 0) { - goto *get; -#define GET_S16_END after -#include "plugin_ops.h" -#undef GET_S16_END - after: - *dst = linear2ulaw(sample); - src += src_step; - dst += dst_step; - } - } -} - -static ssize_t mulaw_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - mulaw_t *data; - unsigned int channel; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0); - assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0); - } - data = (mulaw_t *)plugin->extra_data; - data->func(plugin, src_channels, dst_channels, frames); - return frames; -} - -int snd_pcm_plugin_build_mulaw(snd_pcm_plug_t *plug, - 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; - mulaw_f func; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->rate == dst_format->rate); - assert(src_format->channels == dst_format->channels); - - if (dst_format->format == SND_PCM_SFMT_MU_LAW) { - format = src_format; - func = mulaw_encode; - } - else if (src_format->format == SND_PCM_SFMT_MU_LAW) { - format = dst_format; - func = mulaw_decode; - } - else { - assert(0); - return -EINVAL; - } - assert(snd_pcm_format_linear(format->format)); - - err = snd_pcm_plugin_build(plug, "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); - plugin->transfer = mulaw_transfer; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/rate.c b/src/pcm/plugin/rate.c deleted file mode 100644 index 18f4a9f5..00000000 --- a/src/pcm/plugin/rate.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * Rate conversion Plug-In - * Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz> - * - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include "../pcm_local.h" -#endif - -#define SHIFT 11 -#define BITS (1<<SHIFT) -#define MASK (BITS-1) - -/* - * Basic rate conversion plugin - */ - -typedef struct { - signed short last_S1; - signed short last_S2; -} rate_channel_t; - -typedef void (*rate_f)(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - int src_frames, int dst_frames); - -typedef struct rate_private_data { - unsigned int pitch; - unsigned int pos; - rate_f func; - int get, put; - ssize_t old_src_frames, old_dst_frames; - rate_channel_t channels[0]; -} rate_t; - -static void rate_init(snd_pcm_plugin_t *plugin) -{ - unsigned int channel; - rate_t *data = (rate_t *)plugin->extra_data; - data->pos = 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - data->channels[channel].last_S1 = 0; - data->channels[channel].last_S2 = 0; - } -} - -static void resample_expand(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - int src_frames, int dst_frames) -{ - unsigned int pos = 0; - signed int val; - signed short S1, S2; - char *src, *dst; - unsigned int channel; - int src_step, dst_step; - int src_frames1, dst_frames1; - rate_t *data = (rate_t *)plugin->extra_data; - rate_channel_t *rchannels = data->channels; - -#define GET_S16_LABELS -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS -#undef PUT_S16_LABELS - void *get = get_s16_labels[data->get]; - void *put = put_s16_labels[data->put]; - void *get_s16_end = 0; - signed short sample = 0; -#define GET_S16_END *get_s16_end -#include "plugin_ops.h" -#undef GET_S16_END - - for (channel = 0; channel < plugin->src_format.channels; channel++) { - pos = data->pos; - S1 = rchannels->last_S1; - S2 = rchannels->last_S2; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - src_frames1 = src_frames; - dst_frames1 = dst_frames; - if (pos & ~MASK) { - get_s16_end = &&after_get1; - goto *get; - after_get1: - pos &= MASK; - S1 = S2; - S2 = sample; - src += src_step; - src_frames--; - } - while (dst_frames1-- > 0) { - if (pos & ~MASK) { - pos &= MASK; - S1 = S2; - if (src_frames1-- > 0) { - get_s16_end = &&after_get2; - goto *get; - after_get2: - S2 = sample; - src += src_step; - } - } - val = S1 + ((S2 - S1) * (signed int)pos) / BITS; - if (val < -32768) - val = -32768; - else if (val > 32767) - val = 32767; - sample = val; - goto *put; -#define PUT_S16_END after_put -#include "plugin_ops.h" -#undef PUT_S16_END - after_put: - dst += dst_step; - pos += data->pitch; - } - rchannels->last_S1 = S1; - rchannels->last_S2 = S2; - rchannels++; - } - data->pos = pos; -} - -static void resample_shrink(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - int src_frames, int dst_frames) -{ - unsigned int pos = 0; - signed int val; - signed short S1, S2; - char *src, *dst; - unsigned int channel; - int src_step, dst_step; - int src_frames1, dst_frames1; - rate_t *data = (rate_t *)plugin->extra_data; - rate_channel_t *rchannels = data->channels; - -#define GET_S16_LABELS -#define PUT_S16_LABELS -#include "plugin_ops.h" -#undef GET_S16_LABELS -#undef PUT_S16_LABELS - void *get = get_s16_labels[data->get]; - void *put = put_s16_labels[data->put]; - signed short sample = 0; - - for (channel = 0; channel < plugin->src_format.channels; ++channel) { - pos = data->pos; - S1 = rchannels->last_S1; - S2 = rchannels->last_S2; - if (!src_channels[channel].enabled) { - if (dst_channels[channel].wanted) - snd_pcm_area_silence(&dst_channels[channel].area, 0, dst_frames, plugin->dst_format.format); - dst_channels[channel].enabled = 0; - continue; - } - dst_channels[channel].enabled = 1; - src = (char *)src_channels[channel].area.addr + src_channels[channel].area.first / 8; - dst = (char *)dst_channels[channel].area.addr + dst_channels[channel].area.first / 8; - src_step = src_channels[channel].area.step / 8; - dst_step = dst_channels[channel].area.step / 8; - src_frames1 = src_frames; - dst_frames1 = dst_frames; - while (dst_frames1 > 0) { - S1 = S2; - if (src_frames1-- > 0) { - goto *get; -#define GET_S16_END after_get -#include "plugin_ops.h" -#undef GET_S16_END - after_get: - S2 = sample; - src += src_step; - } - if (pos & ~MASK) { - pos &= MASK; - val = S1 + ((S2 - S1) * (signed int)pos) / BITS; - if (val < -32768) - val = -32768; - else if (val > 32767) - val = 32767; - sample = val; - goto *put; -#define PUT_S16_END after_put -#include "plugin_ops.h" -#undef PUT_S16_END - after_put: - dst += dst_step; - dst_frames1--; - } - pos += data->pitch; - } - rchannels->last_S1 = S1; - rchannels->last_S2 = S2; - rchannels++; - } - data->pos = pos; -} - -static ssize_t rate_src_frames(snd_pcm_plugin_t *plugin, size_t frames) -{ - rate_t *data; - ssize_t res; - - assert(plugin); - if (frames == 0) - return 0; - data = (rate_t *)plugin->extra_data; - if (plugin->src_format.rate < plugin->dst_format.rate) { - res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); - } else { - res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); - } - if (data->old_src_frames > 0) { - ssize_t frames1 = frames, res1 = data->old_dst_frames; - while (data->old_src_frames < frames1) { - frames1 >>= 1; - res1 <<= 1; - } - while (data->old_src_frames > frames1) { - frames1 <<= 1; - res1 >>= 1; - } - if (data->old_src_frames == frames1) - return res1; - } - data->old_src_frames = frames; - data->old_dst_frames = res; - return res; -} - -static ssize_t rate_dst_frames(snd_pcm_plugin_t *plugin, size_t frames) -{ - rate_t *data; - ssize_t res; - - assert(plugin); - if (frames == 0) - return 0; - data = (rate_t *)plugin->extra_data; - if (plugin->src_format.rate < plugin->dst_format.rate) { - res = (((frames << SHIFT) + (data->pitch / 2)) / data->pitch); - } else { - res = (((frames * data->pitch) + (BITS/2)) >> SHIFT); - } - if (data->old_dst_frames > 0) { - ssize_t frames1 = frames, res1 = data->old_src_frames; - while (data->old_dst_frames < frames1) { - frames1 >>= 1; - res1 <<= 1; - } - while (data->old_dst_frames > frames1) { - frames1 <<= 1; - res1 >>= 1; - } - if (data->old_dst_frames == frames1) - return res1; - } - data->old_dst_frames = frames; - data->old_src_frames = res; - return res; -} - -static ssize_t rate_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - size_t dst_frames; - unsigned int channel; - rate_t *data; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - for (channel = 0; channel < plugin->src_format.channels; channel++) { - assert(src_channels[channel].area.first % 8 == 0 && - src_channels[channel].area.step % 8 == 0); - assert(dst_channels[channel].area.first % 8 == 0 && - dst_channels[channel].area.step % 8 == 0); - } - - dst_frames = rate_dst_frames(plugin, frames); - data = (rate_t *)plugin->extra_data; - data->func(plugin, src_channels, dst_channels, frames, dst_frames); - return dst_frames; -} - -static int rate_action(snd_pcm_plugin_t *plugin, - snd_pcm_plugin_action_t action, - unsigned long udata ATTRIBUTE_UNUSED) -{ - assert(plugin); - switch (action) { - case INIT: - case PREPARE: - case DRAIN: - case FLUSH: - rate_init(plugin); - break; - default: - break; - } - return 0; /* silenty ignore other actions */ -} - -int snd_pcm_plugin_build_rate(snd_pcm_plug_t *plug, - 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; - - assert(r_plugin); - *r_plugin = NULL; - - assert(src_format->channels == dst_format->channels); - assert(src_format->channels > 0); - assert(snd_pcm_format_linear(src_format->format) > 0); - assert(snd_pcm_format_linear(dst_format->format) > 0); - assert(src_format->rate != dst_format->rate); - - err = snd_pcm_plugin_build(plug, "rate conversion", - src_format, dst_format, - sizeof(rate_t) + src_format->channels * sizeof(rate_channel_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); - - if (src_format->rate < dst_format->rate) { - data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate; - data->func = resample_expand; - } else { - data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate; - data->func = resample_shrink; - } - data->pos = 0; - rate_init(plugin); - data->old_src_frames = data->old_dst_frames = 0; - plugin->transfer = rate_transfer; - plugin->src_frames = rate_src_frames; - plugin->dst_frames = rate_dst_frames; - plugin->action = rate_action; - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/route.c b/src/pcm/plugin/route.c deleted file mode 100644 index 448c47d8..00000000 --- a/src/pcm/plugin/route.c +++ /dev/null @@ -1,621 +0,0 @@ -/* - * Attenuated route Plug-In - * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> - * - * - * 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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <byteswap.h> -#include <math.h> -#include "../pcm_local.h" -#endif - -/* The best possible hack to support missing optimization in gcc 2.7.2.3 */ -#if ROUTE_PLUGIN_RESOLUTION & (ROUTE_PLUGIN_RESOLUTION - 1) != 0 -#define div(a) a /= ROUTE_PLUGIN_RESOLUTION -#elif ROUTE_PLUGIN_RESOLUTION == 16 -#define div(a) a >>= 4 -#else -#error "Add some code here" -#endif - -typedef struct ttable_dst ttable_dst_t; -typedef struct route_private_data route_t; - -typedef void (*route_channel_f)(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channel, - ttable_dst_t* ttable, size_t frames); - -typedef struct { - int channel; - int as_int; -#if ROUTE_PLUGIN_USE_FLOAT - float as_float; -#endif -} ttable_src_t; - -struct ttable_dst { - int att; /* Attenuated */ - unsigned int nsrcs; - ttable_src_t* srcs; - route_channel_f func; -}; - -struct route_private_data { - enum {UINT32=0, UINT64=1, FLOAT=2} sum_type; - int get, put; - int conv; - int src_sample_size; - ttable_dst_t ttable[0]; -}; - -typedef union { - u_int32_t as_uint32; - u_int64_t as_uint64; -#if ROUTE_PLUGIN_USE_FLOAT - float as_float; -#endif -} sum_t; - - -static void route_to_channel_from_zero(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels ATTRIBUTE_UNUSED, - snd_pcm_plugin_channel_t *dst_channel, - ttable_dst_t* ttable ATTRIBUTE_UNUSED, size_t frames) -{ - if (dst_channel->wanted) - snd_pcm_area_silence(&dst_channel->area, 0, frames, plugin->dst_format.format); - dst_channel->enabled = 0; -} - -static void route_to_channel_from_one(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channel, - ttable_dst_t* ttable, size_t frames) -{ -#define CONV_LABELS -#include "plugin_ops.h" -#undef CONV_LABELS - route_t *data = (route_t *)plugin->extra_data; - void *conv; - const snd_pcm_plugin_channel_t *src_channel = 0; - unsigned int srcidx; - char *src, *dst; - int src_step, dst_step; - for (srcidx = 0; srcidx < ttable->nsrcs; ++srcidx) { - src_channel = &src_channels[ttable->srcs[srcidx].channel]; - if (src_channel->area.addr != NULL) - break; - } - if (srcidx == ttable->nsrcs) { - route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); - return; - } - - dst_channel->enabled = 1; - conv = conv_labels[data->conv]; - src = src_channel->area.addr + src_channel->area.first / 8; - src_step = src_channel->area.step / 8; - dst = dst_channel->area.addr + dst_channel->area.first / 8; - dst_step = dst_channel->area.step / 8; - while (frames-- > 0) { - goto *conv; -#define CONV_END after -#include "plugin_ops.h" -#undef CONV_END - after: - src += src_step; - dst += dst_step; - } -} - -static void route_to_channel(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channel, - ttable_dst_t* ttable, size_t frames) -{ -#define GET_U_LABELS -#define PUT_U32_LABELS -#include "plugin_ops.h" -#undef GET_U_LABELS -#undef PUT_U32_LABELS - static void *zero_labels[3] = { &&zero_int32, &&zero_int64, -#if ROUTE_PLUGIN_USE_FLOAT - &&zero_float -#endif - }; - /* sum_type att */ - static void *add_labels[3 * 2] = { &&add_int32_noatt, &&add_int32_att, - &&add_int64_noatt, &&add_int64_att, -#if ROUTE_PLUGIN_USE_FLOAT - &&add_float_noatt, &&add_float_att -#endif - }; - /* sum_type att shift */ - static void *norm_labels[3 * 2 * 4] = { 0, - &&norm_int32_8_noatt, - &&norm_int32_16_noatt, - &&norm_int32_24_noatt, - 0, - &&norm_int32_8_att, - &&norm_int32_16_att, - &&norm_int32_24_att, - &&norm_int64_0_noatt, - &&norm_int64_8_noatt, - &&norm_int64_16_noatt, - &&norm_int64_24_noatt, - &&norm_int64_0_att, - &&norm_int64_8_att, - &&norm_int64_16_att, - &&norm_int64_24_att, -#if ROUTE_PLUGIN_USE_FLOAT - &&norm_float_0, - &&norm_float_8, - &&norm_float_16, - &&norm_float_24, - &&norm_float_0, - &&norm_float_8, - &&norm_float_16, - &&norm_float_24, -#endif - }; - route_t *data = (route_t *)plugin->extra_data; - void *zero, *get, *add, *norm, *put_u32; - int nsrcs = ttable->nsrcs; - char *dst; - int dst_step; - char *srcs[nsrcs]; - int src_steps[nsrcs]; - ttable_src_t src_tt[nsrcs]; - u_int32_t sample = 0; - int srcidx, srcidx1 = 0; - for (srcidx = 0; srcidx < nsrcs; ++srcidx) { - const snd_pcm_plugin_channel_t *src_channel = &src_channels[ttable->srcs[srcidx].channel]; - if (!src_channel->enabled) - continue; - srcs[srcidx1] = src_channel->area.addr + src_channels->area.first / 8; - src_steps[srcidx1] = src_channel->area.step / 8; - src_tt[srcidx1] = ttable->srcs[srcidx]; - srcidx1++; - } - nsrcs = srcidx1; - if (nsrcs == 0) { - route_to_channel_from_zero(plugin, src_channels, dst_channel, ttable, frames); - return; - } else if (nsrcs == 1 && src_tt[0].as_int == ROUTE_PLUGIN_RESOLUTION) { - route_to_channel_from_one(plugin, src_channels, dst_channel, ttable, frames); - return; - } - - dst_channel->enabled = 1; - zero = zero_labels[data->sum_type]; - get = get_u_labels[data->get]; - add = add_labels[data->sum_type * 2 + ttable->att]; - norm = norm_labels[data->sum_type * 8 + ttable->att * 4 + 4 - data->src_sample_size]; - put_u32 = put_u32_labels[data->put]; - dst = dst_channel->area.addr + dst_channel->area.first / 8; - dst_step = dst_channel->area.step / 8; - - while (frames-- > 0) { - ttable_src_t *ttp = src_tt; - sum_t sum; - - /* Zero sum */ - goto *zero; - zero_int32: - sum.as_uint32 = 0; - goto zero_end; - zero_int64: - sum.as_uint64 = 0; - goto zero_end; -#if ROUTE_PLUGIN_USE_FLOAT - zero_float: - sum.as_float = 0.0; - goto zero_end; -#endif - zero_end: - for (srcidx = 0; srcidx < nsrcs; ++srcidx) { - char *src = srcs[srcidx]; - - /* Get sample */ - goto *get; -#define GET_U_END after_get -#include "plugin_ops.h" -#undef GET_U_END - after_get: - - /* Sum */ - goto *add; - add_int32_att: - sum.as_uint32 += sample * ttp->as_int; - goto after_sum; - add_int32_noatt: - if (ttp->as_int) - sum.as_uint32 += sample; - goto after_sum; - add_int64_att: - sum.as_uint64 += (u_int64_t) sample * ttp->as_int; - goto after_sum; - add_int64_noatt: - if (ttp->as_int) - sum.as_uint64 += sample; - goto after_sum; -#if ROUTE_PLUGIN_USE_FLOAT - add_float_att: - sum.as_float += sample * ttp->as_float; - goto after_sum; - add_float_noatt: - if (ttp->as_int) - sum.as_float += sample; - goto after_sum; -#endif - after_sum: - srcs[srcidx] += src_steps[srcidx]; - ttp++; - } - - /* Normalization */ - goto *norm; - norm_int32_8_att: - sum.as_uint64 = sum.as_uint32; - norm_int64_8_att: - sum.as_uint64 <<= 8; - norm_int64_0_att: - div(sum.as_uint64); - goto norm_int; - - norm_int32_16_att: - sum.as_uint64 = sum.as_uint32; - norm_int64_16_att: - sum.as_uint64 <<= 16; - div(sum.as_uint64); - goto norm_int; - - norm_int32_24_att: - sum.as_uint64 = sum.as_uint32; - norm_int64_24_att: - sum.as_uint64 <<= 24; - div(sum.as_uint64); - goto norm_int; - - norm_int32_8_noatt: - sum.as_uint64 = sum.as_uint32; - norm_int64_8_noatt: - sum.as_uint64 <<= 8; - goto norm_int; - - norm_int32_16_noatt: - sum.as_uint64 = sum.as_uint32; - norm_int64_16_noatt: - sum.as_uint64 <<= 16; - goto norm_int; - - norm_int32_24_noatt: - sum.as_uint64 = sum.as_uint32; - norm_int64_24_noatt: - sum.as_uint64 <<= 24; - goto norm_int; - - norm_int64_0_noatt: - norm_int: - if (sum.as_uint64 > (u_int32_t)0xffffffff) - sample = (u_int32_t)0xffffffff; - else - sample = sum.as_uint64; - goto after_norm; - -#if ROUTE_PLUGIN_USE_FLOAT - norm_float_8: - sum.as_float *= 1 << 8; - goto norm_float; - norm_float_16: - sum.as_float *= 1 << 16; - goto norm_float; - norm_float_24: - sum.as_float *= 1 << 24; - goto norm_float; - norm_float_0: - norm_float: - sum.as_float = floor(sum.as_float + 0.5); - if (sum.as_float > (u_int32_t)0xffffffff) - sample = (u_int32_t)0xffffffff; - else - sample = sum.as_float; - goto after_norm; -#endif - after_norm: - - /* Put sample */ - goto *put_u32; -#define PUT_U32_END after_put_u32 -#include "plugin_ops.h" -#undef PUT_U32_END - after_put_u32: - - dst += dst_step; - } -} - -int route_src_channels_mask(snd_pcm_plugin_t *plugin, - bitset_t *dst_vmask, - bitset_t **src_vmask) -{ - route_t *data = (route_t *)plugin->extra_data; - int schannels = plugin->src_format.channels; - int dchannels = plugin->dst_format.channels; - bitset_t *vmask = plugin->src_vmask; - int channel; - ttable_dst_t *dp = data->ttable; - bitset_zero(vmask, schannels); - for (channel = 0; channel < dchannels; channel++, dp++) { - unsigned int src; - ttable_src_t *sp; - if (!bitset_get(dst_vmask, channel)) - continue; - sp = dp->srcs; - for (src = 0; src < dp->nsrcs; src++, sp++) - bitset_set(vmask, sp->channel); - } - *src_vmask = vmask; - return 0; -} - -int route_dst_channels_mask(snd_pcm_plugin_t *plugin, - bitset_t *src_vmask, - bitset_t **dst_vmask) -{ - route_t *data = (route_t *)plugin->extra_data; - int dchannels = plugin->dst_format.channels; - bitset_t *vmask = plugin->dst_vmask; - int channel; - ttable_dst_t *dp = data->ttable; - bitset_zero(vmask, dchannels); - for (channel = 0; channel < dchannels; channel++, dp++) { - unsigned int src; - ttable_src_t *sp; - sp = dp->srcs; - for (src = 0; src < dp->nsrcs; src++, sp++) { - if (bitset_get(src_vmask, sp->channel)) { - bitset_set(vmask, channel); - break; - } - } - } - *dst_vmask = vmask; - return 0; -} - -static void route_free(snd_pcm_plugin_t *plugin) -{ - route_t *data = (route_t *)plugin->extra_data; - unsigned int dst_channel; - for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { - if (data->ttable[dst_channel].srcs != NULL) - free(data->ttable[dst_channel].srcs); - } -} - -static int route_load_ttable(snd_pcm_plugin_t *plugin, - const route_ttable_entry_t* src_ttable) -{ - route_t *data; - unsigned int src_channel, dst_channel; - const route_ttable_entry_t *sptr; - ttable_dst_t *dptr; - if (src_ttable == NULL) - return 0; - data = (route_t *)plugin->extra_data; - dptr = data->ttable; - sptr = src_ttable; - plugin->private_free = route_free; - for (dst_channel = 0; dst_channel < plugin->dst_format.channels; ++dst_channel) { - route_ttable_entry_t t = 0; - int att = 0; - int nsrcs = 0; - ttable_src_t srcs[plugin->src_format.channels]; - for (src_channel = 0; src_channel < plugin->src_format.channels; ++src_channel) { - assert(*sptr >= 0 && *sptr <= FULL); - if (*sptr != 0) { - srcs[nsrcs].channel = src_channel; -#if ROUTE_PLUGIN_USE_FLOAT - /* Also in user space for non attenuated */ - srcs[nsrcs].as_int = (*sptr == FULL ? ROUTE_PLUGIN_RESOLUTION : 0); - srcs[nsrcs].as_float = *sptr; -#else - srcs[nsrcs].as_int = *sptr; -#endif - if (*sptr != FULL) - att = 1; - t += *sptr; - nsrcs++; - } - sptr++; - } -#if 0 - assert(t <= FULL); -#endif - dptr->att = att; - dptr->nsrcs = nsrcs; - switch (nsrcs) { - case 0: - dptr->func = route_to_channel_from_zero; - break; - case 1: - dptr->func = route_to_channel_from_one; - break; - default: - dptr->func = route_to_channel; - break; - } - if (nsrcs > 0) { - dptr->srcs = calloc(nsrcs, sizeof(*srcs)); - memcpy(dptr->srcs, srcs, sizeof(*srcs) * nsrcs); - } else - dptr->srcs = 0; - dptr++; - } - return 0; -} - -static ssize_t route_transfer(snd_pcm_plugin_t *plugin, - const snd_pcm_plugin_channel_t *src_channels, - snd_pcm_plugin_channel_t *dst_channels, - size_t frames) -{ - route_t *data; - int src_nchannels, dst_nchannels; - int src_channel, dst_channel; - ttable_dst_t *ttp; - snd_pcm_plugin_channel_t *dvp; - - assert(plugin && src_channels && dst_channels); - if (frames == 0) - return 0; - data = (route_t *)plugin->extra_data; - - src_nchannels = plugin->src_format.channels; - for (src_channel = 0; src_channel < src_nchannels; ++src_channel) { - assert(src_channels[src_channel].area.first % 8 == 0 && - src_channels[src_channel].area.step % 8 == 0); - } - - dst_nchannels = plugin->dst_format.channels; - for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { - assert(dst_channels[dst_channel].area.first % 8 == 0 && - dst_channels[dst_channel].area.step % 8 == 0); - } - - ttp = data->ttable; - dvp = dst_channels; - for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { - ttp->func(plugin, src_channels, dvp, ttp, frames); - dvp++; - ttp++; - } - return frames; -} - -int getput_index(int format) -{ - int sign, width, endian; - sign = !snd_pcm_format_signed(format); - width = snd_pcm_format_width(format) / 8 - 1; -#ifdef SND_LITTLE_ENDIAN - endian = snd_pcm_format_big_endian(format); -#else - endian = snd_pcm_format_little_endian(format); -#endif - if (endian < 0) - endian = 0; - return width * 4 + endian * 2 + sign; -} - -#ifndef __KERNEL__ -static void route_dump(snd_pcm_plugin_t *plugin, FILE *fp) -{ - route_t *data; - ttable_dst_t *tdp; - unsigned int dst_channel, dst_nchannels; - data = (route_t *)plugin->extra_data; - tdp = data->ttable; - dst_nchannels = plugin->dst_format.channels; - for (dst_channel = 0; dst_channel < dst_nchannels; ++dst_channel) { - unsigned int k; - ttable_src_t *tsp = tdp->srcs; - fprintf(fp, "Channel %d = ", dst_channel); - for (k = 0; k < tdp->nsrcs; ++k) { - if (k > 0) - fprintf(fp, " + "); - fprintf(fp, "[%d]", tsp->channel); - if (tdp->att) { -#if ROUTE_PLUGIN_USE_FLOAT - fprintf(fp, "*%g", tsp->as_float); -#else - fprintf(fp, "*%d/%d", tsp->as_int, ROUTE_PLUGIN_RESOLUTION); -#endif - } - ++tsp; - } - fprintf(fp, "\n"); - ++tdp; - } -} -#endif - -int snd_pcm_plugin_build_route(snd_pcm_plug_t *plug, - snd_pcm_format_t *src_format, - snd_pcm_format_t *dst_format, - route_ttable_entry_t *ttable, - snd_pcm_plugin_t **r_plugin) -{ - route_t *data; - snd_pcm_plugin_t *plugin; - int err; - - assert(r_plugin); - *r_plugin = NULL; - assert(src_format->rate == dst_format->rate); - assert(snd_pcm_format_linear(src_format->format) && - snd_pcm_format_linear(dst_format->format)); - - err = snd_pcm_plugin_build(plug, "attenuated route conversion", - src_format, dst_format, - sizeof(route_t) + sizeof(data->ttable[0]) * dst_format->channels, - &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->conv = conv_index(src_format->format, dst_format->format); - -#if ROUTE_PLUGIN_USE_FLOAT - data->sum_type = FLOAT; -#else - if (snd_pcm_format_width(src_format->format) == 32) - data->sum_type = UINT64; - else - data->sum_type = UINT32; -#endif - data->src_sample_size = snd_pcm_format_width(src_format->format) / 8; - - if ((err = route_load_ttable(plugin, ttable)) < 0) { - snd_pcm_plugin_free(plugin); - return err; - } - plugin->transfer = route_transfer; - plugin->src_channels_mask = route_src_channels_mask; - plugin->dst_channels_mask = route_dst_channels_mask; -#ifndef __KERNEL__ - plugin->dump = route_dump; -#endif - *r_plugin = plugin; - return 0; -} diff --git a/src/pcm/plugin/plugin_ops.h b/src/pcm/plugin_ops.h index ed91a2c1..ed91a2c1 100644 --- a/src/pcm/plugin/plugin_ops.h +++ b/src/pcm/plugin_ops.h diff --git a/src/rawmidi/rawmidi.c b/src/rawmidi/rawmidi.c index f733a35c..1a0cad07 100644 --- a/src/rawmidi/rawmidi.c +++ b/src/rawmidi/rawmidi.c @@ -86,7 +86,7 @@ int snd_rawmidi_close(snd_rawmidi_t *rmidi) return res; } -int snd_rawmidi_file_descriptor(snd_rawmidi_t *rmidi) +int snd_rawmidi_poll_descriptor(snd_rawmidi_t *rmidi) { if (!rmidi) return -EINVAL; diff --git a/src/seq/seq.c b/src/seq/seq.c index 771f18b3..ebf16e40 100644 --- a/src/seq/seq.c +++ b/src/seq/seq.c @@ -119,7 +119,7 @@ int snd_seq_close(snd_seq_t *seq) /* * returns the file descriptor of the client */ -int snd_seq_file_descriptor(snd_seq_t *seq) +int snd_seq_poll_descriptor(snd_seq_t *seq) { if (!seq) return -EINVAL; diff --git a/src/timer/timer.c b/src/timer/timer.c index 41f42d7c..7201b0cf 100644 --- a/src/timer/timer.c +++ b/src/timer/timer.c @@ -78,7 +78,7 @@ int snd_timer_close(snd_timer_t *handle) return res; } -int snd_timer_file_descriptor(snd_timer_t *handle) +int snd_timer_poll_descriptor(snd_timer_t *handle) { snd_timer_t *tmr; diff --git a/test/latency.c b/test/latency.c index 961d1e68..6daf446b 100644 --- a/test/latency.c +++ b/test/latency.c @@ -47,11 +47,11 @@ int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle, int *bufsize) params.mode = SND_PCM_MODE_FRAME; #endif params.format.interleave = 1; - params.format.format = SND_PCM_SFMT_S16_LE; + params.format.sfmt = SND_PCM_SFMT_S16_LE; params.format.channels = 2; params.format.rate = USED_RATE; - params.start_mode = SND_PCM_START_GO; - params.xrun_mode = SND_PCM_XRUN_DRAIN; + params.start_mode = SND_PCM_START_EXPLICIT; + params.xrun_action = SND_PCM_XRUN_ACT_DRAIN; params.time = 1; *bufsize += 4; @@ -152,7 +152,7 @@ long readbuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) long r; do { - r = snd_pcm_read(handle, buf, len); + r = snd_pcm_readi(handle, buf, len); } while (r == -EAGAIN); if (r > 0) *frames += r; @@ -166,7 +166,7 @@ long writebuf(snd_pcm_t *handle, char *buf, long len, size_t *frames) long r; while (len > 0) { - r = snd_pcm_write(handle, buf, len); + r = snd_pcm_writei(handle, buf, len); if (r == -EAGAIN) continue; // printf("write = %li\n", r); @@ -225,7 +225,7 @@ int main(void) break; } - if ((err = snd_pcm_go(phandle)) < 0) { + if ((err = snd_pcm_start(phandle)) < 0) { printf("Go error: %s\n", snd_strerror(err)); exit(0); } diff --git a/test/loopback.c b/test/loopback.c index d9e0cdbe..83265955 100644 --- a/test/loopback.c +++ b/test/loopback.c @@ -15,7 +15,7 @@ static void show_format1(const char *prefix, snd_pcm_format_t *format) format->interleave ? 1 : 0, format->rate, format->voices, - format->format); + format->sfmt); } static void show_format(snd_pcm_loopback_t *handle) diff --git a/test/pause.c b/test/pause.c index 7c865ab0..0da03421 100644 --- a/test/pause.c +++ b/test/pause.c @@ -39,7 +39,7 @@ int main(void) fprintf(stderr, "open failed: %s\n", snd_strerror(err)); return 0; } - format.format = SND_PCM_SFMT_MU_LAW; + format.sfmt = SND_PCM_SFMT_MU_LAW; format.rate = 8000; format.channels = 1; if ((err = snd_pcm_playback_format(handle, &format)) < 0) { @@ -82,7 +82,7 @@ int main(void) } count1 = status.fragment_size * 12; show_playback_status(handle); - size = snd_pcm_write(handle, buffer1, count1); + size = snd_pcm_writei(handle, buffer1, count1); sleep(2); show_playback_status(handle); printf("Pause.. Bytes written %i from %i...\n", size, count1); @@ -94,7 +94,7 @@ int main(void) printf("Pause end..\n"); snd_pcm_playback_pause(handle, 0); show_playback_status(handle); - size = snd_pcm_write(handle, buffer1, count); + size = snd_pcm_writei(handle, buffer1, count); printf("Pause end.. Bytes written %i from %i...\n", size, count); snd_pcm_close(handle); free(buffer); @@ -18,7 +18,7 @@ void setformat(void *phandle, void *rhandle) snd_pcm_format_t format; bzero(&format, sizeof(format)); - format.format = SND_PCM_SFMT_S16_LE; + format.sfmt = SND_PCM_SFMT_S16_LE; format.channels = 2; format.rate = 22050; if ((err = snd_pcm_playback_format(phandle, &format)) < 0) { @@ -46,14 +46,14 @@ void method1(void) setformat(phandle, rhandle); printf("Recording... "); fflush(stdout); - if ((err = snd_pcm_read(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_readi(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } printf("done...\n"); printf("Playback... "); fflush(stdout); - if ((err = snd_pcm_write(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_writei(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } @@ -81,7 +81,7 @@ void method2(void) setformat(phandle, rhandle); printf("Recording... "); fflush(stdout); - if ((err = snd_pcm_read(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_readi(rhandle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } @@ -93,7 +93,7 @@ void method2(void) printf("Record flush done...\n"); printf("Playback... "); fflush(stdout); - if ((err = snd_pcm_write(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_writei(phandle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } @@ -122,7 +122,7 @@ void method3(void) setformat(handle, handle); printf("Recording... "); fflush(stdout); - if ((err = snd_pcm_read(handle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_readi(handle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Read error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } @@ -134,7 +134,7 @@ void method3(void) printf("Record flush done...\n"); printf("Playback... "); fflush(stdout); - if ((err = snd_pcm_write(handle, buffer, sizeof(buffer))) != sizeof(buffer)) { + if ((err = snd_pcm_writei(handle, buffer, sizeof(buffer))) != sizeof(buffer)) { printf("Write error: %s\n", err < 0 ? snd_strerror(err) : xitoa(err)); return; } diff --git a/test/playmidi1.c b/test/playmidi1.c index 85c76bd9..c2e6ec9e 100644 --- a/test/playmidi1.c +++ b/test/playmidi1.c @@ -116,7 +116,7 @@ static void write_ev(snd_seq_event_t *ev) while ((rc = snd_seq_event_output(seq_handle, ev)) < 0) { int seqfd; fd_set fds; - seqfd = snd_seq_file_descriptor(seq_handle); + seqfd = snd_seq_poll_descriptor(seq_handle); FD_ZERO(&fds); FD_SET(seqfd, &fds); if ((rc = select(seqfd + 1, NULL, &fds, NULL, NULL)) < 0) { @@ -366,7 +366,7 @@ static snd_seq_event_t *wait_for_event(void) input_event == NULL) { int seqfd; fd_set fds; - seqfd = snd_seq_file_descriptor(seq_handle); + seqfd = snd_seq_poll_descriptor(seq_handle); FD_ZERO(&fds); FD_SET(seqfd, &fds); if ((left = select(seqfd + 1, &fds, NULL, NULL, NULL)) < 0) { diff --git a/test/seq-decoder.c b/test/seq-decoder.c index a87943a3..e3b075e0 100644 --- a/test/seq-decoder.c +++ b/test/seq-decoder.c @@ -481,7 +481,7 @@ void event_decoder(snd_seq_t *handle, int argc, char *argv[]) while (1) { FD_ZERO(&in); - FD_SET(max = snd_seq_file_descriptor(handle), &in); + FD_SET(max = snd_seq_poll_descriptor(handle), &in); if (select(max + 1, &in, NULL, NULL, NULL) < 0) break; do { diff --git a/test/seq-sender.c b/test/seq-sender.c index d00eaf5b..8905ac82 100644 --- a/test/seq-sender.c +++ b/test/seq-sender.c @@ -14,7 +14,7 @@ void set_format(snd_pcm_t *phandle) snd_pcm_format_t format; bzero(&format, sizeof(format)); - format.format = SND_PCM_SFMT_S16_LE; + format.sfmt = SND_PCM_SFMT_S16_LE; format.channels = 2; format.rate = 44100; if ((err = snd_pcm_playback_format(phandle, &format)) < 0) { @@ -225,31 +225,31 @@ void event_sender(snd_seq_t *handle, int argc, char *argv[]) while (1) { FD_ZERO(&out); FD_ZERO(&in); - max = snd_seq_file_descriptor(handle); - FD_SET(snd_seq_file_descriptor(handle), &in); + max = snd_seq_poll_descriptor(handle); + FD_SET(snd_seq_poll_descriptor(handle), &in); if (snd_seq_event_output_pending(handle)) { - FD_SET(snd_seq_file_descriptor(handle), &out); + FD_SET(snd_seq_poll_descriptor(handle), &out); } #ifdef USE_PCM if (phandle) { - if (snd_pcm_file_descriptor(phandle) > max) - max = snd_pcm_file_descriptor(phandle); - FD_SET(snd_pcm_file_descriptor(phandle), &out); + if (snd_pcm_poll_descriptor(phandle) > max) + max = snd_pcm_poll_descriptor(phandle); + FD_SET(snd_pcm_poll_descriptor(phandle), &out); } #endif if (select(max + 1, &in, &out, NULL, NULL) < 0) break; #ifdef USE_PCM - if (phandle && FD_ISSET(snd_pcm_file_descriptor(phandle), &out)) { - if (snd_pcm_write(phandle, pbuf, pfragment_size) != pfragment_size) { + if (phandle && FD_ISSET(snd_pcm_poll_descriptor(phandle), &out)) { + if (snd_pcm_writei(phandle, pbuf, pfragment_size) != pfragment_size) { fprintf(stderr, "Playback write error!!\n"); exit(0); } } #endif - if (FD_ISSET(snd_seq_file_descriptor(handle), &out)) + if (FD_ISSET(snd_seq_poll_descriptor(handle), &out)) snd_seq_flush_output(handle); - if (FD_ISSET(snd_seq_file_descriptor(handle), &in)) { + if (FD_ISSET(snd_seq_poll_descriptor(handle), &in)) { do { if ((err = snd_seq_event_input(handle, &ev))<0) break; diff --git a/test/timer.c b/test/timer.c index 99fa7813..24953014 100644 --- a/test/timer.c +++ b/test/timer.c @@ -28,7 +28,7 @@ void read_loop(void *handle, int master_ticks, int timeout) while (master_ticks-- > 0) { FD_ZERO(&in); - max = snd_timer_file_descriptor(handle); + max = snd_timer_poll_descriptor(handle); FD_SET(max, &in); tv.tv_sec = timeout; tv.tv_usec = 0; |