]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Merged pcmplug branch.
authorJaroslav Kysela <perex@perex.cz>
Sun, 24 Sep 2000 09:57:26 +0000 (09:57 +0000)
committerJaroslav Kysela <perex@perex.cz>
Sun, 24 Sep 2000 09:57:26 +0000 (09:57 +0000)
61 files changed:
TODO
acinclude.m4
include/aserver.h
include/control.h
include/header.h
include/hwdep.h
include/mixer.h
include/pcm.h
include/rawmidi.h
include/seq.h
include/timer.h
src/Makefile.am
src/aserver/aserver.c
src/conf/conf.c
src/control/control.c
src/control/control_client.c
src/control/control_hw.c
src/control/control_local.h
src/hwdep/hwdep.c
src/mixer/mixer.c
src/pcm/Makefile.am
src/pcm/pcm.c
src/pcm/pcm_adpcm.c [new file with mode: 0644]
src/pcm/pcm_alaw.c [new file with mode: 0644]
src/pcm/pcm_client.c
src/pcm/pcm_common.c [deleted file]
src/pcm/pcm_file.c [new file with mode: 0644]
src/pcm/pcm_hw.c
src/pcm/pcm_linear.c [new file with mode: 0644]
src/pcm/pcm_local.h
src/pcm/pcm_misc.c
src/pcm/pcm_mmap.c
src/pcm/pcm_mulaw.c [new file with mode: 0644]
src/pcm/pcm_multi.c
src/pcm/pcm_plug.c
src/pcm/pcm_plugin.c [new file with mode: 0644]
src/pcm/pcm_plugin.h [new file with mode: 0644]
src/pcm/pcm_rate.c [new file with mode: 0644]
src/pcm/pcm_route.c [new file with mode: 0644]
src/pcm/plugin/Makefile.am [deleted file]
src/pcm/plugin/adpcm.c [deleted file]
src/pcm/plugin/alaw.c [deleted file]
src/pcm/plugin/copy.c [deleted file]
src/pcm/plugin/io.c [deleted file]
src/pcm/plugin/linear.c [deleted file]
src/pcm/plugin/mmap.c [deleted file]
src/pcm/plugin/mulaw.c [deleted file]
src/pcm/plugin/rate.c [deleted file]
src/pcm/plugin/route.c [deleted file]
src/pcm/plugin_ops.h [moved from src/pcm/plugin/plugin_ops.h with 100% similarity]
src/rawmidi/rawmidi.c
src/seq/seq.c
src/timer/timer.c
test/latency.c
test/loopback.c
test/pause.c
test/pcm.c
test/playmidi1.c
test/seq-decoder.c
test/seq-sender.c
test/timer.c

diff --git a/TODO b/TODO
index 99b877390c85bfaee54c43586388ef3790c91968..4f523d91a25510a56d0c75059d1df27036c5ae3e 100644 (file)
--- a/TODO
+++ b/TODO
@@ -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)
index 59ef07e94bdd70385307ff4ad1ec24034b68be02..1bf50ad9e79e9c2eaf15255173be1aa952ba1e35 100644 (file)
@@ -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);
index fb7164ed40b60c39d00de742f94a7b96adf6966a..aef553a861faf9a4a4d1df44fc721b73fd7f3387 100644 (file)
  */
   
 
-#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;
index f68b5ac0ee7c6297b3eaa920e485bd0d4df0f49c..07ad251ebfc8928c11185bbdd8b2d1ecbf10c551 100644 (file)
@@ -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);
index 6c667bd26b773fc4c849e6edcb7942329dd228bc..6875e256890fb3b5e5b972a06ec412e7f5f40224 100644 (file)
@@ -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__))
index 81918192608349c989a162ce55fc19cf6dd85cbf..6446543b1b729c872844264b3693eb0eda4a9b22 100644 (file)
@@ -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);
index 1daef64e97cbb242b40f65d1abd4ca88136aa0cc..f7eab69b29427352700e5b90f272db0dbff5caa3 100644 (file)
@@ -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
 }
index 25a20e023f4447a6dad20ffd212197fe7fb8fd7b..a559176e5b09cb2f15ab6a27daf41c5794830cdb 100644 (file)
@@ -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
-
index 087b88217efffe2f4b1662ffc21d32e875d4673e..5bdab9763290d2c690def1c44723ab5bb242b9c9 100644 (file)
@@ -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);
index 7e358060e8c68991f74e767ccf0f117b06d7b0c5..3bd4989d88d42751268497279e9c824d9af1b290 100644 (file)
@@ -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);
index 49c69437469469f3d3672e7efd36ce1a191ef641..a406d9c00d5c72091b6ca904bb47c5a8991e2568 100644 (file)
@@ -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);
index 60eb86129b1952405b86605beb788896bac058b6..c6985ed6da725ccf77e5d1376daa08785965b170 100644 (file)
@@ -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)
 
index a4e7449bd3feb4e9665abc4e5734ce182906e249..4a77a95203e4f9846e8c395acb1fda6e4467d015 100644 (file)
@@ -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) {
index 4607b6d5b6fd1675f5e23c9392394109091b00cf..de4e6bfbf437b11f5c9f07cfef10ac19e93ed20c 100644 (file)
@@ -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] == '-')) {
index bfd38045f468ce7d5c5aacf68969e2d835e6976c..2ed0f38b8ffa607d1e659177b085b6a93656d66d 100644 (file)
@@ -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)
index 264f4b3a854741ef40c08e4c809df51f12dd1ef1..ef323e6004d00ed791b6e4c7ccfc25f193cee490 100644 (file)
@@ -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,
index 496ce220531f680fddeebb4c7adc5d9be61e49f2..8ca8b8b518501064df5a56988acfc45b6ad4b85a 100644 (file)
@@ -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,
index d396df5a47b61d7f76f6e0137d987ec12e8232e2..cf1a5828127f2ad5395b2a8a03e97ec659f26236 100644 (file)
@@ -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);
index dfcbb9368029b2dddd4389bc437ef2ba0216253c..e644053666e2256c5b0245ed6de153d1eb5ce44b 100644 (file)
@@ -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;
index ed22bf34dc644e2b863faf7d1d84568eaf03d756..b534ffdb5cc1fc875121f1ffbaff68a4ca1159c0 100644 (file)
@@ -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)
index f08c836998ba1f0d2f307c4bfaa6b8dc105e227e..82da9063212a1898506acd2537f48761801e064b 100644 (file)
@@ -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
index 82a7a218d5df6967147c9884c1c2768816df3368..fbf8bff7a73efc5eb2eeeb864a9d47b43999cbce 100644 (file)
 #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 (file)
index 0000000..f02fbae
--- /dev/null
@@ -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 (file)
index 0000000..570de59
--- /dev/null
@@ -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;
+}
+                               
+
index d0b865bcb95e96d11e06bf58078e820136460c43..7e7799ab59f4dd47f5966fa4209fa56b203eb5c2 100644 (file)
@@ -290,222 +290,117 @@ 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 (file)
index ae947ec..0000000
+++ /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 (file)
index 0000000..644aae3
--- /dev/null
@@ -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;
+}
index 01d9429131e17c1601c7e383deddf53d8b686ac9..d3adac9f427b5158a6ac2c6c84b8e6c2e5ef3878 100644 (file)
@@ -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 (file)
index 0000000..277be5b
--- /dev/null
@@ -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;
+}
+                               
+
index 8cabad136ef6a93a2dcebca9ad1e6b40f65e55a1..35154c7abda4a50be113e293a4a435cc8ef11d13 100644 (file)
  */
 
 #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;
+}
+
index b829f5c97dbb388004135dfaff6887e41a5c6c86..57bfcf21aac7782cc8559f7e98996c8e6a7f91fe 100644 (file)
@@ -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>
index fdb5a08e8d677d95e949fa2e50b6496dfa71e4c7..2be47c537288d9d5763ddb169ef854dc88fbe394 100644 (file)
 #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 (file)
index 0000000..14fc3c3
--- /dev/null
@@ -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;
+}
+                               
+
index 433e58f807dc916f29b6fc5f64a669f4a29ccf13..f697bd953cdd46c1d618049a456350fd95028762 100644 (file)
@@ -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;
+}
+
index adb41aae0814caac10386e6d4da3236f269928a5..adf491fa4b0d70889df73c43aae1409c93c55d14 100644 (file)
  *
  */
   
-#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 = &params->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(&params->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 (file)
index 0000000..2825703
--- /dev/null
@@ -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 (file)
index 0000000..e76993c
--- /dev/null
@@ -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 (file)
index 0000000..104019f
--- /dev/null
@@ -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 (file)
index 0000000..406f9f3
--- /dev/null
@@ -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 (file)
index 5d79297..0000000
+++ /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 (file)
index efac226..0000000
+++ /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 (file)
index b563b01..0000000
+++ /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 (file)
index de4762d..0000000
+++ /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 (file)
index 6ef33cd..0000000
+++ /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 (file)
index 83b5e9b..0000000
+++ /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 (file)
index db1f6a4..0000000
+++ /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 (file)
index 74beeb3..0000000
+++ /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 (file)
index 18f4a9f..0000000
+++ /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 (file)
index 448c47d..0000000
+++ /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;
-}
index f733a35cff69da877f0d67c63fce30fd964d125e..1a0cad0711806b1c2a9fada9fb0ccce7b1b0a8cb 100644 (file)
@@ -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;
index 771f18b3e1da43eef558daa524002692cd9f9166..ebf16e406656f26f49dd3c9ede90bcfcac77d64f 100644 (file)
@@ -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;
index 41f42d7c3a37064532d0938bd10cd700139184ad..7201b0cfd2f2837c71a2c9b3f00376ddd04bd439 100644 (file)
@@ -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;
 
index 961d1e68bf9c7dc9593caa021a09591998853779..6daf446b1fcc54077c2dd3774cc084b8b67c2d24 100644 (file)
@@ -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);
                }
index d9e0cdbe5c2441a0c729ea4eaefbd2331ecad34b..83265955b72c71fb6cc0c84bef4d412587833e83 100644 (file)
@@ -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)
index 7c865ab0799041b7278e7fc6aec87041320ae92d..0da03421c408a3d2d36f4e5cbb036657b2abb561 100644 (file)
@@ -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);
index 643a0c19773bb4274848697ac0686d92058a773f..d48c25c411a9d444fea4e309904f25c426b88d78 100644 (file)
@@ -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;
        }
index 85c76bd9484e7bcc28f4d4f6424960287bc75a21..c2e6ec9ee25d09da5f80613f5ead2413f91bea4f 100644 (file)
@@ -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) {
index a87943a39421b9b2760e88c79f93315db0b76d20..e3b075e0ae17d63c05ac836c3182e635c08aee81 100644 (file)
@@ -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 {
index d00eaf5b3b6a9949ae1fb366d5caccd06d581b8e..8905ac824307d19a4516e3a92d92f80a6dfc33ad 100644 (file)
@@ -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;
index 99fa7813b9f29b2ccac62a7e38943666c507eeb1..24953014a8bd4ff90b8e02293820c93dede7a253 100644 (file)
@@ -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;