OSDN Git Service

Merged pcm2 branch.
authorJaroslav Kysela <perex@perex.cz>
Mon, 8 May 2000 18:53:38 +0000 (18:53 +0000)
committerJaroslav Kysela <perex@perex.cz>
Mon, 8 May 2000 18:53:38 +0000 (18:53 +0000)
40 files changed:
acinclude.m4
cvscompile
include/footer.h
include/instr.h
include/pcm.h
include/seq.h
src/Makefile.am
src/error.c
src/hwdep/hwdep.c
src/instr/iwffff.c
src/instr/simple.c
src/mixer/elements.c
src/mixer/mixer.c
src/pcm/Makefile.am
src/pcm/pcm.c
src/pcm/pcm_hw.c [new file with mode: 0644]
src/pcm/pcm_local.h
src/pcm/pcm_loopback.c [deleted file]
src/pcm/pcm_misc.c
src/pcm/pcm_mmap.c [new file with mode: 0644]
src/pcm/pcm_plug.c [new file with mode: 0644]
src/pcm/pcm_plugin.c [deleted file]
src/pcm/pcm_plugin_build.c
src/pcm/plugin/Makefile.am
src/pcm/plugin/adpcm.c
src/pcm/plugin/alaw.c
src/pcm/plugin/block.c
src/pcm/plugin/copy.c [new file with mode: 0644]
src/pcm/plugin/linear.c
src/pcm/plugin/mmap.c
src/pcm/plugin/mulaw.c
src/pcm/plugin/plugin_ops.h
src/pcm/plugin/rate.c
src/pcm/plugin/route.c
src/pcm/plugin/stream.c
src/rawmidi/rawmidi.c
src/seq/seq.c
src/seq/seq_priv.h
src/timer/timer.c
test/latency.c

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