OSDN Git Service

Completed linear plugin
authorAbramo Bagnara <abramo@alsa-project.org>
Sat, 25 Dec 1999 15:25:46 +0000 (15:25 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Sat, 25 Dec 1999 15:25:46 +0000 (15:25 +0000)
src/pcm/pcm_plugin_build.c
src/pcm/plugin/linear.c

index 1fe6040..98c398a 100644 (file)
@@ -101,112 +101,96 @@ ssize_t snd_pcm_plugin_hardware_size(PLUGIN_BASE *pb, int channel, size_t trf_si
 
 unsigned int snd_pcm_plugin_formats(unsigned int formats)
 {
+       int linfmts = (SND_PCM_FMT_U8 | SND_PCM_FMT_S8 |
+                      SND_PCM_FMT_U16_LE | SND_PCM_FMT_S16_LE |
+                      SND_PCM_FMT_U16_BE | SND_PCM_FMT_S16_BE |
+                      SND_PCM_FMT_U24_LE | SND_PCM_FMT_S16_LE |
+                      SND_PCM_FMT_U24_BE | SND_PCM_FMT_S16_BE |
+                      SND_PCM_FMT_U32_LE | SND_PCM_FMT_S32_LE |
+                      SND_PCM_FMT_U32_BE | SND_PCM_FMT_S32_BE);
        formats |= SND_PCM_FMT_MU_LAW;
 #ifndef __KERNEL__
        formats |= SND_PCM_FMT_A_LAW | SND_PCM_FMT_IMA_ADPCM;
 #endif
-       if (formats & (SND_PCM_FMT_U8|SND_PCM_FMT_S8|
-                      SND_PCM_FMT_U16_LE|SND_PCM_FMT_S16_LE))
-               formats |= SND_PCM_FMT_U8|SND_PCM_FMT_S8|
-                          SND_PCM_FMT_U16_LE|SND_PCM_FMT_S16_LE;
+       
+       if (formats & linfmts)
+               formats |= linfmts;
        return formats;
 }
 
+static int preferred_formats[] = {
+       SND_PCM_SFMT_S16_LE,
+       SND_PCM_SFMT_S16_BE,
+       SND_PCM_SFMT_U16_LE,
+       SND_PCM_SFMT_U16_BE,
+       SND_PCM_SFMT_S24_LE,
+       SND_PCM_SFMT_S24_BE,
+       SND_PCM_SFMT_U24_LE,
+       SND_PCM_SFMT_U24_BE,
+       SND_PCM_SFMT_S32_LE,
+       SND_PCM_SFMT_S32_BE,
+       SND_PCM_SFMT_U32_LE,
+       SND_PCM_SFMT_U32_BE,
+       SND_PCM_SFMT_S8,
+       SND_PCM_SFMT_U8
+};
+
 int snd_pcm_plugin_hwparams(snd_pcm_channel_params_t *params,
                            snd_pcm_channel_info_t *hwinfo,
                            snd_pcm_channel_params_t *hwparams)
 {
        memcpy(hwparams, params, sizeof(*hwparams));
        if ((hwinfo->formats & (1 << params->format.format)) == 0) {
-               if ((snd_pcm_plugin_formats(hwinfo->formats) & (1 << params->format.format)) == 0)
+               int format = params->format.format;
+               if ((snd_pcm_plugin_formats(hwinfo->formats) & (1 << format)) == 0)
                        return -EINVAL;
-               switch (params->format.format) {
-               case SND_PCM_SFMT_U8:
-                       if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
-               case SND_PCM_SFMT_S8:
-                       if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
-               case SND_PCM_SFMT_S16_LE:
-                       if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else {
-                               return -EINVAL;
+               if (snd_pcm_format_linear(format)) {
+                       int width = snd_pcm_format_width(format);
+                       int unsignd = snd_pcm_format_unsigned(format);
+                       int big = snd_pcm_format_big_endian(format);
+                       int format1;
+                       int wid, width1=width;
+                       for (wid = 0; wid < 4; ++wid) {
+                               int end, big1 = big;
+                               for (end = 0; end < 2; ++end) {
+                                       int sgn, unsignd1 = unsignd;
+                                       for (sgn = 0; sgn < 2; ++sgn) {
+                                               format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
+                                               if (format1 >= 0 &&
+                                                   hwinfo->formats & (1 << format1))
+                                                       goto _found;
+                                               unsignd1 = !unsignd1;
+                                       }
+                                       big1 = !big1;
+                               }
+                               width1 += 8;
+                               if (width1 > 32)
+                                       width = 8;
                        }
-                       break;
-               case SND_PCM_SFMT_U16_LE:
-                       if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
-               case SND_PCM_SFMT_MU_LAW:
-                       if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else {
-                               return -EINVAL;
-                       }
-                       break;
+                       return -EINVAL;
+               _found:
+                       hwparams->format.format = format1;
+               } else {
+                       int i;
+                       switch (format) {
+                       case SND_PCM_SFMT_MU_LAW:
 #ifndef __KERNEL__
-               case SND_PCM_SFMT_A_LAW:
-                       if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else {
-                               return -EINVAL;
-                       }
-               case SND_PCM_SFMT_IMA_ADPCM:
-                       if (hwinfo->formats & SND_PCM_FMT_S16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U16_LE) {
-                               hwparams->format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (hwinfo->formats & SND_PCM_FMT_S8) {
-                               hwparams->format.format = SND_PCM_SFMT_S8;
-                       } else if (hwinfo->formats & SND_PCM_FMT_U8) {
-                               hwparams->format.format = SND_PCM_SFMT_U8;
-                       } else {
+                       case SND_PCM_SFMT_A_LAW:
+                       case SND_PCM_SFMT_IMA_ADPCM:
+#endif
+                               for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
+                                       int format1 = preferred_formats[i];
+                                       if (hwinfo->formats & (1 << format1)) {
+                                               hwparams->format.format = format1;
+                                               break;
+                                       }
+                               }
+                               if (i == sizeof(preferred_formats)/sizeof(preferred_formats[0]))
+                                       return -EINVAL;
+                               break;
+                       default:
                                return -EINVAL;
                        }
-                       break;
-#endif
-               default:
-                       return -EINVAL;
                }
        }
 
index d5173ab..7fbdf6a 100644 (file)
@@ -23,6 +23,8 @@
 #include "../../include/driver.h"
 #include "../../include/pcm.h"
 #include "../../include/pcm_plugin.h"
+#define bswap_16(x) __swab16((x))
+#define bswap_32(x) __swab32((x))
 #else
 #include <stdio.h>
 #include <stdlib.h>
  *  Basic linear conversion plugin
  */
  
-typedef enum {
-       _8BIT_16BIT,
-       _8BIT_24BIT,
-       _8BIT_32BIT,
-       _16BIT_8BIT,
-       _16BIT_24BIT,
-       _16BIT_32BIT,
-       _24BIT_8BIT,
-       _24BIT_16BIT,
-       _24BIT_32BIT,
-       _32BIT_8BIT,
-       _32BIT_16BIT,
-       _32BIT_24BIT
-} combination_t;
-typedef enum {
-       NONE = 0,
-       SOURCE = 1,
-       DESTINATION = 2,
-       BOTH = 3,
-       SIGN = 4,
-       SIGN_NONE = 4,
-       SIGN_SOURCE = 5,
-       SIGN_DESTINATION = 6,
-       SIGN_BOTH = 7,
-} endian_t;
+typedef void (*conv_f)(void *src, void *dst, size_t size);
+
 struct linear_private_data {
-       combination_t cmd;
-       endian_t endian;
+       int src_sample_size, dst_sample_size;
+       conv_f func;
 };
 
-static void linear_conv_8bit_16bit(unsigned char *src_ptr,
-                                  unsigned short *dst_ptr,
-                                  size_t size)
-{
-       while (size--)
-               *dst_ptr++ = ((unsigned short)*src_ptr++) << 8;
+#define CONV_FUNC(name, srctype, dsttype, val) \
+static void conv_##name(void *src_ptr, void *dst_ptr, size_t size) \
+{ \
+       unsigned srctype *srcp = src_ptr; \
+       unsigned dsttype *dstp = dst_ptr; \
+       while (size--) { \
+               unsigned srctype src = *srcp++; \
+               *dstp++ = val; \
+       } \
 }
 
-static void linear_conv_8bit_16bit_swap(unsigned char *src_ptr,
-                                       unsigned short *dst_ptr,
-                                       size_t size)
-{
-       while (size--)
-               *dst_ptr++ = (unsigned short)*src_ptr++;
-}
+CONV_FUNC(8_sign, char, char, src ^ 0x80)
 
-static void linear_conv_sign_8bit_16bit(unsigned char *src_ptr,
-                                       unsigned short *dst_ptr,
-                                       size_t size)
-{
-       while (size--)
-               *dst_ptr++ = (((unsigned short)*src_ptr++) << 8) ^ 0x8000;
-}
+CONV_FUNC(8_16, char, short, (unsigned short)src << 8)
+CONV_FUNC(8_16_end, char, short, (unsigned short)src)
+CONV_FUNC(8_16_sign, char, short, (unsigned short)(src ^ 0x80) << 8)
+CONV_FUNC(8_16_sign_end, char, short, (unsigned short)src ^ 0x80)
 
-static void linear_conv_sign_8bit_16bit_swap(unsigned char *src_ptr,
-                                            unsigned short *dst_ptr,
-                                            size_t size)
-{
-       while (size--)
-               *dst_ptr++ = ((unsigned short)*src_ptr++) ^ 0x80;
-}
+CONV_FUNC(8_32, char, long, (unsigned long)src << 24)
+CONV_FUNC(8_32_end, char, long, (unsigned long)src)
+CONV_FUNC(8_32_sign, char, long, (unsigned long)(src ^ 0x80) << 24)
+CONV_FUNC(8_32_sign_end, char, long, (unsigned long)src ^ 0x80)
 
-static void linear_conv_16bit_8bit(unsigned short *src_ptr,
-                                  unsigned char *dst_ptr,
-                                  size_t size)
-{
-       while (size--)
-               *dst_ptr++ = (*src_ptr++) >> 8;
-}
+CONV_FUNC(16_8, short, char, src >> 8)
+CONV_FUNC(16_end_8, short, char, src)
+CONV_FUNC(16_8_sign, short, char, (src >> 8) ^ 0x80)
+CONV_FUNC(16_end_8_sign, short, char, src ^ 0x80;)
 
-static void linear_conv_16bit_8bit_swap(unsigned short *src_ptr,
-                                       unsigned char *dst_ptr,
-                                       size_t size)
-{
-       while (size--)
-               *dst_ptr++ = (unsigned char)*src_ptr++;
-}
+CONV_FUNC(16_sign, short, short, src ^ 0x8000)
+CONV_FUNC(16_end, short, short, bswap_16(src))
+CONV_FUNC(16_end_sign, short, short, bswap_16(src) ^ 0x8000)
+CONV_FUNC(16_sign_end, short, short, bswap_16(src ^ 0x8000))
+CONV_FUNC(16_end_sign_end, short, short, src ^ 0x80)
 
-static void linear_conv_sign_16bit_8bit(unsigned short *src_ptr,
-                                       unsigned char *dst_ptr,
-                                       size_t size)
-{
-       while (size--)
-               *dst_ptr++ = (((unsigned short)*src_ptr++) >> 8) ^ 0x80;
-}
+CONV_FUNC(16_32, short, long, (unsigned long)src << 16)
+CONV_FUNC(16_32_sign, short, long, (unsigned long)(src ^ 0x8000) << 16)
+CONV_FUNC(16_32_end, short, long, (unsigned long)bswap_16(src))
+CONV_FUNC(16_32_sign_end, short, long, (unsigned long)bswap_16(src ^ 0x8000))
+CONV_FUNC(16_end_32, short, long, (unsigned long)bswap_16(src) << 16)
+CONV_FUNC(16_end_32_sign, short, long, (unsigned long)(bswap_16(src) ^ 0x8000) << 16)
+CONV_FUNC(16_end_32_end, short, long, (unsigned long)src)
+CONV_FUNC(16_end_32_sign_end, short, long, (unsigned long)src ^ 0x80)
+
+CONV_FUNC(32_8, long, char, src >> 24)
+CONV_FUNC(32_end_8, long, char, src)
+CONV_FUNC(32_8_sign, long, char, (src >> 24) ^ 0x80)
+CONV_FUNC(32_end_8_sign, long, char, src ^ 0x80;)
+
+CONV_FUNC(32_16, long, short, src >> 16)
+CONV_FUNC(32_16_sign, long, short, (src >> 16) ^ 0x8000)
+CONV_FUNC(32_16_end, long, short, bswap_16(src >> 16))
+CONV_FUNC(32_16_sign_end, long, short, bswap_16((src >> 16) ^ 0x8000))
+CONV_FUNC(32_end_16, long, short, bswap_16(src))
+CONV_FUNC(32_end_16_sign, long, short, bswap_16(src) ^ 0x8000)
+CONV_FUNC(32_end_16_end, long, short, src)
+CONV_FUNC(32_end_16_sign_end, long, short, src ^ 0x80)
+
+CONV_FUNC(32_sign, long, long, src ^ 0x80000000)
+CONV_FUNC(32_end, long, long, bswap_32(src))
+CONV_FUNC(32_end_sign, long, long, bswap_32(src) ^ 0x80000000)
+CONV_FUNC(32_sign_end, long, long, bswap_32(src) ^ 0x80)
+CONV_FUNC(32_end_sign_end, long, long, src ^ 0x80)
+
+/* src_wid dst_wid src_endswap, dst_endswap, sign_swap */
+conv_f convert_functions[3][3][2][2][2] = {
+       NULL,                   /* 8->8: Nothing to do */
+       conv_8_sign,            /* 8->8 sign: conv_8_sign */
+       NULL,                   /* 8->8 dst_end: Nothing to do */
+       conv_8_sign,            /* 8->8 dst_end sign: conv_8_sign */
+       NULL,                   /* 8->8 src_end: Nothing to do */
+       conv_8_sign,            /* 8->8 src_end sign: conv_8_sign */
+       NULL,                   /* 8->8 src_end dst_end: Nothing to do */
+       conv_8_sign,            /* 8->8 src_end dst_end sign: conv_8_sign */
+       conv_8_16,              /* 8->16: conv_8_16 */
+       conv_8_16_sign,         /* 8->16 sign: conv_8_16_sign */
+       conv_8_16_end,          /* 8->16 dst_end: conv_8_16_end */
+       conv_8_16_sign_end,     /* 8->16 dst_end sign: conv_8_16_sign_end */
+       conv_8_16,              /* 8->16 src_end: conv_8_16 */
+       conv_8_16_sign,         /* 8->16 src_end sign: conv_8_16_sign */
+       conv_8_16_end,          /* 8->16 src_end dst_end: conv_8_16_end */
+       conv_8_16_sign_end,     /* 8->16 src_end dst_end sign: conv_8_16_sign_end */
+       conv_8_32,                      /* 8->32: conv_8_32 */
+       conv_8_32_sign,                 /* 8->32 sign: conv_8_32_sign */
+       conv_8_32_end,                  /* 8->32 dst_end: conv_8_32_end */
+       conv_8_32_sign_end,                     /* 8->32 dst_end sign: conv_8_32_sign_end */
+       conv_8_32,                      /* 8->32 src_end: conv_8_32 */
+       conv_8_32_sign,                 /* 8->32 src_end sign: conv_8_32_sign */
+       conv_8_32_end,                  /* 8->32 src_end dst_end: conv_8_32_end */
+       conv_8_32_sign_end,                     /* 8->32 src_end dst_end sign: conv_8_32_sign_end */
+       conv_16_8,              /* 16->8: conv_16_8 */
+       conv_16_8_sign,         /* 16->8 sign: conv_16_8_sign */
+       conv_16_8,              /* 16->8 dst_end: conv_16_8 */
+       conv_16_8_sign,         /* 16->8 dst_end sign: conv_16_8_sign */
+       conv_16_end_8,          /* 16->8 src_end: conv_16_end_8 */
+       conv_16_end_8_sign,     /* 16->8 src_end sign: conv_16_end_8_sign */
+       conv_16_end_8,          /* 16->8 src_end dst_end: conv_16_end_8 */
+       conv_16_end_8_sign,     /* 16->8 src_end dst_end sign: conv_16_end_8_sign */
+       NULL,                   /* 16->16: Nothing to do */
+       conv_16_sign,           /* 16->16 sign: conv_16_sign */
+       conv_16_end,            /* 16->16 dst_end: conv_16_end */
+       conv_16_sign_end,       /* 16->16 dst_end sign: conv_16_sign_end */
+       conv_16_end,            /* 16->16 src_end: conv_16_end */
+       conv_16_end_sign,       /* 16->16 src_end sign: conv_16_end_sign */
+       NULL,                   /* 16->16 src_end dst_end: Nothing to do */
+       conv_16_end_sign_end,   /* 16->16 src_end dst_end sign: conv_16_end_sign_end */
+       conv_16_32,             /* 16->32: conv_16_32 */
+       conv_16_32_sign,        /* 16->32 sign: conv_16_32_sign */
+       conv_16_32_end,         /* 16->32 dst_end: conv_16_32_end */
+       conv_16_32_sign_end,    /* 16->32 dst_end sign: conv_16_32_sign_end */
+       conv_16_end_32,         /* 16->32 src_end: conv_16_end_32 */
+       conv_16_end_32_sign,    /* 16->32 src_end sign: conv_16_end_32_sign */
+       conv_16_end_32_end,     /* 16->32 src_end dst_end: conv_16_end_32_end */
+       conv_16_end_32_sign_end,/* 16->32 src_end dst_end sign: conv_16_end_32_sign_end */
+       conv_32_8,              /* 32->8: conv_32_8 */
+       conv_32_8_sign,         /* 32->8 sign: conv_32_8_sign */
+       conv_32_8,              /* 32->8 dst_end: conv_32_8 */
+       conv_32_8_sign,         /* 32->8 dst_end sign: conv_32_8_sign */
+       conv_32_end_8,          /* 32->8 src_end: conv_32_end_8 */
+       conv_32_end_8_sign,     /* 32->8 src_end sign: conv_32_end_8_sign */
+       conv_32_end_8,          /* 32->8 src_end dst_end: conv_32_end_8 */
+       conv_32_end_8_sign,     /* 32->8 src_end dst_end sign: conv_32_end_8_sign */
+       conv_32_16,             /* 32->16: conv_32_16 */
+       conv_32_16_sign,        /* 32->16 sign: conv_32_16_sign */
+       conv_32_16_end,         /* 32->16 dst_end: conv_32_16_end */
+       conv_32_16_sign_end,    /* 32->16 dst_end sign: conv_32_16_sign_end */
+       conv_32_end_16,         /* 32->16 src_end: conv_32_end_16 */
+       conv_32_end_16_sign,    /* 32->16 src_end sign: conv_32_end_16_sign */
+       conv_32_end_16_end,     /* 32->16 src_end dst_end: conv_32_end_16_end */
+       conv_32_end_16_sign_end,/* 32->16 src_end dst_end sign: conv_32_end_16_sign_end */
+       NULL,                   /* 32->32: Nothing to do */
+       conv_32_sign,           /* 32->32 sign: conv_32_sign */
+       conv_32_end,            /* 32->32 dst_end: conv_32_end */
+       conv_32_sign_end,       /* 32->32 dst_end sign: conv_32_sign_end */
+       conv_32_end,            /* 32->32 src_end: conv_32_end */
+       conv_32_end_sign,       /* 32->32 src_end sign: conv_32_end_sign */
+       NULL,                   /* 32->32 src_end dst_end: Nothing to do */
+       conv_32_end_sign_end    /* 32->32 src_end dst_end sign: conv_32_end_sign_end */
+};
 
-static void linear_conv_sign_16bit_8bit_swap(unsigned short *src_ptr,
-                                            unsigned char *dst_ptr,
-                                            size_t size)
-{
-       while (size--)
-               *dst_ptr++ = ((unsigned char)*src_ptr++) ^ 0x80;
-}
 
 static ssize_t linear_transfer(snd_pcm_plugin_t *plugin,
                               char *src_ptr, size_t src_size,
@@ -148,51 +201,12 @@ static ssize_t linear_transfer(snd_pcm_plugin_t *plugin,
        data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin);
        if (data == NULL)
                return -EINVAL;
-       switch (data->cmd) {
-       case _8BIT_16BIT:
-               if ((dst_size >> 1) < src_size)
-                       return -EINVAL;
-               switch (data->endian) {
-               case NONE:
-                       linear_conv_8bit_16bit(src_ptr, (short *)dst_ptr, src_size);
-                       break;
-               case DESTINATION:
-                       linear_conv_8bit_16bit_swap(src_ptr, (short *)dst_ptr, src_size);
-                       break;
-               case SIGN_NONE:
-                       linear_conv_sign_8bit_16bit(src_ptr, (short *)dst_ptr, src_size);
-                       break;
-               case SIGN_DESTINATION:
-                       linear_conv_sign_8bit_16bit_swap(src_ptr, (short *)dst_ptr, src_size);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               return src_size << 1;
-       case _16BIT_8BIT:
-               if (dst_size < (src_size >> 1))
-                       return -EINVAL;
-               src_size >>= 1;
-               switch (data->endian) {
-               case NONE:
-                       linear_conv_16bit_8bit((short *)src_ptr, dst_ptr, src_size);
-                       break;
-               case SOURCE:
-                       linear_conv_16bit_8bit_swap((short *)src_ptr, dst_ptr, src_size);
-                       break;
-               case SIGN_NONE:
-                       linear_conv_sign_16bit_8bit((short *)src_ptr, dst_ptr, src_size);
-                       break;
-               case SIGN_SOURCE:
-                       linear_conv_sign_16bit_8bit_swap((short *)src_ptr, dst_ptr, src_size);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-               return src_size;
-       default:
-               return -EIO;
-       }
+       if (src_size % data->src_sample_size != 0)
+               return -EINVAL;
+       if (dst_size < src_size*data->dst_sample_size/data->src_sample_size)
+               return -EINVAL;
+       data->func(src_ptr, dst_ptr, src_size / data->src_sample_size);
+       return src_size*data->dst_sample_size/data->src_sample_size;
 }
 
 static ssize_t linear_src_size(snd_pcm_plugin_t *plugin, size_t size)
@@ -202,27 +216,9 @@ static ssize_t linear_src_size(snd_pcm_plugin_t *plugin, size_t size)
        if (!plugin || size <= 0)
                return -EINVAL;
        data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin);
-       switch (data->cmd) {
-       case _8BIT_16BIT:
-       case _16BIT_24BIT:
-       case _16BIT_32BIT:
-               return size / 2;
-       case _8BIT_24BIT:
-       case _8BIT_32BIT:
-               return size / 4;
-       case _16BIT_8BIT:
-       case _24BIT_16BIT:
-       case _32BIT_16BIT:
-               return size * 2;
-       case _24BIT_8BIT:
-       case _32BIT_8BIT:
-               return size * 4;
-       case _24BIT_32BIT:
-       case _32BIT_24BIT:
-               return size;
-       default:
-               return -EIO;
-       }
+       if (data == NULL)
+               return -EINVAL;
+       return size*data->src_sample_size/data->dst_sample_size;
 }
 
 static ssize_t linear_dst_size(snd_pcm_plugin_t *plugin, size_t size)
@@ -232,27 +228,9 @@ static ssize_t linear_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        if (!plugin || size <= 0)
                return -EINVAL;
        data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin);
-       switch (data->cmd) {
-       case _8BIT_16BIT:
-       case _16BIT_24BIT:
-       case _16BIT_32BIT:
-               return size * 2;
-       case _8BIT_24BIT:
-       case _8BIT_32BIT:
-               return size * 4;
-       case _16BIT_8BIT:
-       case _24BIT_16BIT:
-       case _32BIT_16BIT:
-               return size / 2;
-       case _24BIT_8BIT:
-       case _32BIT_8BIT:
-               return size / 4;
-       case _24BIT_32BIT:
-       case _32BIT_24BIT:
-               return size;
-       default:
-               return -EIO;
-       }
+       if (data == NULL)
+               return -EINVAL;
+       return size*data->dst_sample_size/data->src_sample_size;
 }
 
 int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format,
@@ -261,8 +239,8 @@ int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format,
 {
        struct linear_private_data *data;
        snd_pcm_plugin_t *plugin;
-       combination_t cmd;
-       int width1, width2, endian1, endian2, sign1, sign2;
+       conv_f func;
+       int src_endian, dst_endian, sign, src_width, dst_width;
 
        if (!r_plugin)
                return -EINVAL;
@@ -279,69 +257,66 @@ int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format,
              snd_pcm_format_linear(dst_format->format)))
                return -EINVAL;
 
-       width1 = snd_pcm_format_width(src_format->format);
-       sign1 = snd_pcm_format_signed(src_format->format);
-       width2 = snd_pcm_format_width(dst_format->format);
-       sign2 = snd_pcm_format_signed(dst_format->format);
+       sign = (snd_pcm_format_signed(src_format->format) !=
+               snd_pcm_format_signed(dst_format->format));
+       src_width = snd_pcm_format_width(src_format->format);
+       dst_width = snd_pcm_format_width(dst_format->format);
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-       endian1 = snd_pcm_format_little_endian(src_format->format);
-       endian2 = snd_pcm_format_little_endian(dst_format->format);
+       src_endian = snd_pcm_format_big_endian(src_format->format);
+       dst_endian = snd_pcm_format_big_endian(dst_format->format);
 #elif __BYTE_ORDER == __BIG_ENDIAN
-       endian1 = snd_pcm_format_big_endian(src_format->format);
-       endian2 = snd_pcm_format_big_endian(dst_format->format);
+       src_endian = snd_pcm_format_little_endian(src_format->format);
+       dst_endian = snd_pcm_format_little_endian(dst_format->format);
 #else
 #error "Unsupported endian..."
 #endif
-       cmd = _8BIT_16BIT;
-       switch (width1) {
+
+       switch (src_width) {
        case 8:
-               switch (width2) {
-               case 16:        cmd = _8BIT_16BIT; break;
-               case 24:        cmd = _8BIT_24BIT; break;
-               case 32:        cmd = _8BIT_32BIT; break;
-               default:        return -EINVAL;
-               }
+               src_width = 0;
                break;
        case 16:
-               switch (width2) {
-               case 8:         cmd = _16BIT_8BIT; break;
-               case 24:        cmd = _16BIT_24BIT; break;
-               case 32:        cmd = _16BIT_32BIT; break;
-               default:        return -EINVAL;
-               }
+               src_width = 1;
                break;
-       case 24:
-               switch (width2) {
-               case 8:         cmd = _24BIT_8BIT; break;
-               case 16:        cmd = _24BIT_16BIT; break;
-               case 32:        cmd = _24BIT_32BIT; break;
-               default:        return -EINVAL;
-               }
+       case 32:
+               src_width = 2;
+               break;
+       default:
+               return -EINVAL;
+       }
+       switch (dst_width) {
+       case 8:
+               dst_width = 0;
+               break;
+       case 16:
+               dst_width = 1;
                break;
        case 32:
-               switch (width2) {
-               case 8:         cmd = _32BIT_8BIT; break;
-               case 16:        cmd = _32BIT_16BIT; break;
-               case 24:        cmd = _32BIT_24BIT; break;
-               default:        return -EINVAL;
-               }
+               dst_width = 2;
                break;
        default:
                return -EINVAL;
        }
+
+       if (src_endian < 0)
+               src_endian = 0;
+       if (dst_endian < 0)
+               src_endian = 0;
+
+       func = convert_functions[src_width][dst_width][src_endian][dst_endian][sign];
+
+       if (func == NULL)
+               return -EINVAL;
+
        plugin = snd_pcm_plugin_build("linear format conversion",
                                      sizeof(struct linear_private_data));
        if (plugin == NULL)
                return -ENOMEM;
        data = (struct linear_private_data *)snd_pcm_plugin_extra_data(plugin);
-       data->cmd = cmd;
-       data->endian = NONE;
-       if (endian1 == 0)
-               data->endian |= SOURCE;
-       if (endian2 == 0)
-               data->endian |= DESTINATION;
-       if (sign1 != sign2)
-               data->endian |= SIGN;
+       data->func = func;
+       data->src_sample_size = 1 << src_width;
+       data->dst_sample_size = 1 << dst_width;
+
        plugin->transfer = linear_transfer;
        plugin->src_size = linear_src_size;
        plugin->dst_size = linear_dst_size;