]> git.alsa-project.org Git - alsa-lib.git/commitdiff
PCM plugin patches made by Abramo Bagnara.
authorJaroslav Kysela <perex@perex.cz>
Thu, 2 Dec 1999 14:31:26 +0000 (14:31 +0000)
committerJaroslav Kysela <perex@perex.cz>
Thu, 2 Dec 1999 14:31:26 +0000 (14:31 +0000)
Added new plugin - voice & balance.

include/pcm.h
src/pcm/pcm_plugin.c
src/pcm/plugin/Makefile.am
src/pcm/plugin/adpcm.c
src/pcm/plugin/alaw.c
src/pcm/plugin/interleave.c
src/pcm/plugin/linear.c
src/pcm/plugin/mulaw.c
src/pcm/plugin/rate.c
src/pcm/plugin/voices.c
src/pcm/plugin/volbal.c [new file with mode: 0644]

index 00f8c3a2afd401c0e36fd6a9aec0c365ec7f4a3c..13b8c9dac9a85b9321a357d1e68f593d3119ec00 100644 (file)
@@ -122,17 +122,31 @@ int snd_pcm_plugin_build_stream(snd_pcm_t *handle, int channel, snd_pcm_plugin_t
 int snd_pcm_plugin_build_block(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin);
 int snd_pcm_plugin_build_mmap(snd_pcm_t *handle, int channel, snd_pcm_plugin_t **r_plugin);
 /* conversion plugins */
-int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int format, snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices,
-                             int dst_format, int dst_rate, int dst_voices,
+int snd_pcm_plugin_build_interleave(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_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
+                               snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_mulaw(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_format_t *src_format,
+                             snd_pcm_format_t *dst_format,
+                             snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_adpcm(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_format_t *src_format,
+                             snd_pcm_format_t *dst_format,
                              snd_pcm_plugin_t **r_plugin);
-int snd_pcm_plugin_build_voices(int src_format, int src_voices,
-                               int dst_format, int dst_voices,
+int snd_pcm_plugin_build_voices(snd_pcm_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
                                snd_pcm_plugin_t **r_plugin);
+int snd_pcm_plugin_build_volbal(snd_pcm_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
+                               int *ttable,
+                               snd_pcm_plugin_t **r_plugin);
 
 /*
  *  Loopback interface
index ce3cc17412fdbd58619fc5f2da13dd4363b5abfb..7f54f235852203ee0f92a4cabef71bf02f52b9b5 100644 (file)
@@ -285,123 +285,111 @@ static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action)
        return 0;
 }
 
-static void swap_formats(int channel, int *src, int *dst)
-{
-       int tmp;
-
-       if (channel == SND_PCM_CHANNEL_PLAYBACK)
-               return;
-       tmp = *src;
-       *src = *dst;
-       *dst = tmp;     
-}
-
-#define CONVERT_RATIO(dest, ratio) dest = ((int)(((double)dest * ratio)+0.5))
-
 int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
 {
-       double ratio;
-       snd_pcm_channel_params_t sparams;
-       snd_pcm_channel_info_t sinfo;
+       snd_pcm_channel_params_t hwparams;
+       snd_pcm_channel_params_t srcparams, tmpparams;
+       snd_pcm_channel_params_t *dstparams;
+       snd_pcm_channel_info_t hwinfo;
        snd_pcm_plugin_t *plugin;
-       int err, srcfmt, dstfmt;
+       int err;
        
        if (!pcm || !params || params->channel < 0 || params->channel > 1)
                return -EINVAL;
-       memcpy(&sparams, params, sizeof(sparams));
+       memcpy(&hwparams, params, sizeof(hwparams));
 
        /*
         *  try to decide, if a conversion is required
          */
 
-       memset(&sinfo, 0, sizeof(sinfo));
-       sinfo.channel = params->channel;
-       if ((err = snd_pcm_channel_info(pcm, &sinfo)) < 0) {
+       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 ((sinfo.formats & (1 << sparams.format.format)) == 0) {
-               if ((snd_pcm_plugin_formats(pcm, sinfo.formats) & (1 << sparams.format.format)) == 0)
+       if ((hwinfo.formats & (1 << params->format.format)) == 0) {
+               if ((snd_pcm_plugin_formats(pcm, hwinfo.formats) & (1 << params->format.format)) == 0)
                        return -EINVAL;
-               switch (sparams.format.format) {
+               switch (params->format.format) {
                case SND_PCM_SFMT_U8:
-                       if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
-                       } else if (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
+                       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 (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
-                       } else if (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
+                       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 (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
-                       } else if (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
+                       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;
                case SND_PCM_SFMT_U16_LE:
-                       if (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
-                       } else if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
+                       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 (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
-                       } else if (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
+                       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;
                case SND_PCM_SFMT_A_LAW:
-                       if (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
-                       } else if (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
+                       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 (sinfo.formats & SND_PCM_FMT_S16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_S16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_U16_LE) {
-                               sparams.format.format = SND_PCM_SFMT_U16_LE;
-                       } else if (sinfo.formats & SND_PCM_FMT_S8) {
-                               sparams.format.format = SND_PCM_SFMT_S8;
-                       } else if (sinfo.formats & SND_PCM_FMT_U8) {
-                               sparams.format.format = SND_PCM_SFMT_U8;
+                       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;
                        }
@@ -411,25 +399,58 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, 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;
+       }
+
+       /* 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;
+       }
+
+       /* interleave */
+       hwparams.format.interleave = params->format.interleave;
+       if (!(hwinfo.flags & SND_PCM_CHNINFO_INTERLEAVE))
+               hwparams.format.interleave = 0;
+       if (!(hwinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE))
+               hwparams.format.interleave = 1;
+
        /*
         *  add necessary plugins
         */
 
        snd_pcm_plugin_clear(pcm, params->channel);
 
-       if (sparams.format.voices < sinfo.min_voices ||
-           sparams.format.voices > sinfo.max_voices) {
-               int src_voices = sparams.format.voices;
-               int dst_voices = sparams.format.voices < sinfo.min_voices ?
-                                sinfo.min_voices : sinfo.max_voices;
-               if (sparams.format.rate < sinfo.min_rate ||
-                   sparams.format.rate > sinfo.max_rate)
-                       dst_voices = 2;
-               sparams.format.voices = dst_voices;
-               swap_formats(params->channel, &src_voices, &dst_voices);
-               err = snd_pcm_plugin_build_voices(params->format.format, src_voices,
-                                                 params->format.format, dst_voices,
-                                                 &plugin);
+       if (params->channel == SND_PCM_CHANNEL_PLAYBACK) {
+               memcpy(&srcparams, params, sizeof(srcparams));
+               memcpy(&tmpparams, params, sizeof(tmpparams));
+               dstparams = &hwparams;
+       } else {
+               memcpy(&srcparams, &hwparams, sizeof(srcparams));
+               memcpy(&tmpparams, &hwparams, sizeof(tmpparams));
+               dstparams = params;
+       }
+
+       /* Convert to interleaved format if needed */
+       if (!srcparams.format.interleave &&
+           (srcparams.format.voices != dstparams->format.voices ||
+            (srcparams.format.rate != dstparams->format.rate &&
+             srcparams.format.voices > 1))) {
+               tmpparams.format.interleave = 1;
+               err = snd_pcm_plugin_build_interleave(&srcparams.format,
+                                                     &tmpparams.format,
+                                                     &plugin);
                if (err < 0) {
                        snd_pcm_plugin_free(plugin);
                        return err;
@@ -439,64 +460,69 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                        snd_pcm_plugin_free(plugin);
                        return err;
                }
+               srcparams.format.interleave = 1;
+               /* Avoid useless interleave revert */
+               if (params->channel == SND_PCM_CHANNEL_PLAYBACK &&
+                   (hwinfo.flags & SND_PCM_CHNINFO_INTERLEAVE))
+                       dstparams->format.interleave = 1;
        }
 
-       /*
-        *  formats
-        */
-      
-       if (params->format.format == sparams.format.format)
-               goto __format_skip;
-       /* build additional plugins for conversion */
-       switch (params->format.format) {
-       case SND_PCM_SFMT_MU_LAW:
-               srcfmt = SND_PCM_SFMT_MU_LAW;
-               dstfmt = sparams.format.format;
-               swap_formats(params->channel, &srcfmt, &dstfmt);
-               err = snd_pcm_plugin_build_mulaw(srcfmt, dstfmt, &plugin);
-               break;
-       case SND_PCM_SFMT_A_LAW:
-               srcfmt = SND_PCM_SFMT_A_LAW;
-               dstfmt = sparams.format.format;
-               swap_formats(params->channel, &srcfmt, &dstfmt);
-               err = snd_pcm_plugin_build_alaw(srcfmt, dstfmt, &plugin);
-               break;
-       case SND_PCM_SFMT_IMA_ADPCM:
-               srcfmt = SND_PCM_SFMT_IMA_ADPCM;
-               dstfmt = sparams.format.format;
-               swap_formats(params->channel, &srcfmt, &dstfmt);
-               err = snd_pcm_plugin_build_adpcm(srcfmt, dstfmt, &plugin);
-               break;
-       default:
-               srcfmt = params->format.format;
-               dstfmt = sparams.format.format;
-               swap_formats(params->channel, &srcfmt, &dstfmt);
-               err = snd_pcm_plugin_build_linear(srcfmt, dstfmt, &plugin);
-       }
-       if (err < 0)
-               return err;
-       err = snd_pcm_plugin_append(pcm, params->channel, plugin);
-       if (err < 0) {
-               snd_pcm_plugin_free(plugin);
-               return err;
-       }
-
-      __format_skip:
+       /* voices reduction  */
+       if (srcparams.format.voices > dstparams->format.voices) {
+               tmpparams.format.voices = dstparams->format.voices;
+               err = snd_pcm_plugin_build_voices(&srcparams.format,
+                                                 &tmpparams.format,
+                                                 &plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
+               }
+               err = snd_pcm_plugin_append(pcm, params->channel, plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
+               }
+               srcparams.format.voices = dstparams->format.voices;
+        }
 
-       /*
-        *  rate
-        */
+       /* format change */
+       if (srcparams.format.format != dstparams->format.format) {
+               switch (params->format.format) {
+               case SND_PCM_SFMT_MU_LAW:
+                       err = snd_pcm_plugin_build_mulaw(&srcparams.format,
+                                                        &tmpparams.format,
+                                                        &plugin);
+                       break;
+               case SND_PCM_SFMT_A_LAW:
+                       err = snd_pcm_plugin_build_alaw(&srcparams.format,
+                                                       &tmpparams.format,
+                                                       &plugin);
+                       break;
+               case SND_PCM_SFMT_IMA_ADPCM:
+                       err = snd_pcm_plugin_build_adpcm(&srcparams.format,
+                                                        &tmpparams.format,
+                                                        &plugin);
+                       break;
+               default:
+                       err = snd_pcm_plugin_build_linear(&srcparams.format,
+                                                         &tmpparams.format,
+                                                         &plugin);
+               }
+               if (err < 0)
+                       return err;
+               err = snd_pcm_plugin_append(pcm, params->channel, plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
+               }
+       }
 
-        if (sparams.format.rate < sinfo.min_rate ||
-            sparams.format.rate > sinfo.max_rate) {
-               int src_rate = sparams.format.rate;
-               int dst_rate = sparams.format.rate < sinfo.min_rate ?
-                              sinfo.min_rate : sinfo.max_rate;
-               sparams.format.rate = dst_rate;
-               swap_formats(params->channel, &src_rate, &dst_rate);
-               err = snd_pcm_plugin_build_rate(sparams.format.format, src_rate, sparams.format.voices,
-                                               sparams.format.format, dst_rate, sparams.format.voices,
-                                               &plugin);
+       /* rate resampling */
+        if (srcparams.format.rate != dstparams->format.rate) {
+               tmpparams.format.rate = dstparams->format.rate;
+               err = snd_pcm_plugin_build_rate(&srcparams.format,
+                                               &tmpparams.format,
+                                               &plugin);
                if (err < 0) {
                        snd_pcm_plugin_free(plugin);
                        return err;
@@ -506,46 +532,55 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                        snd_pcm_plugin_free(plugin);
                        return err;
                }
+               srcparams.format.rate = dstparams->format.rate;
         }
       
-       /*
-        *  interleave
-         */
-      
-       if (params->format.voices > 1 && sinfo.mode == SND_PCM_MODE_BLOCK) {
-               int src_interleave, dst_interleave;
-
-               if (params->format.interleave) {
-                       src_interleave = dst_interleave = 1;
-                       if (!(sinfo.flags & SND_PCM_CHNINFO_INTERLEAVE))
-                               dst_interleave = 0;
-               } else {
-                       src_interleave = dst_interleave = 0;
-                       if (!(sinfo.flags & SND_PCM_CHNINFO_NONINTERLEAVE))
-                               dst_interleave = 1;
+       /* voices extension  */
+       if (srcparams.format.voices < dstparams->format.voices) {
+               tmpparams.format.voices = dstparams->format.voices;
+               err = snd_pcm_plugin_build_voices(&srcparams.format,
+                                                 &tmpparams.format,
+                                                 &plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
+               }                                           
+               err = snd_pcm_plugin_append(pcm, params->channel, plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
                }
-               if (src_interleave != dst_interleave) {
-                       sparams.format.interleave = dst_interleave;
-                       swap_formats(params->channel, &src_interleave, &dst_interleave);
-                       err = snd_pcm_plugin_build_interleave(src_interleave, dst_interleave, sparams.format.format, &plugin);
-                       if (err < 0)
-                               return err;
-                       err = snd_pcm_plugin_append(pcm, params->channel, plugin);
-                       if (err < 0) {
-                               snd_pcm_plugin_free(plugin);
-                               return err;
-                       }
+               srcparams.format.voices = dstparams->format.voices;
+       }
+
+       /* interleave change */
+       if (params->format.voices > 1 && 
+           hwinfo.mode == SND_PCM_MODE_BLOCK &&
+           srcparams.format.interleave != dstparams->format.interleave) {
+               tmpparams.format.interleave = dstparams->format.interleave;
+               err = snd_pcm_plugin_build_interleave(&srcparams.format,
+                                                     &tmpparams.format,
+                                                     &plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
+               }                                           
+               err = snd_pcm_plugin_append(pcm, params->channel, plugin);
+               if (err < 0) {
+                       snd_pcm_plugin_free(plugin);
+                       return err;
                }
+               srcparams.format.interleave = dstparams->format.interleave;
        }
 
        /*
         *  I/O plugins
         */
 
-       if (sinfo.mode == SND_PCM_MODE_STREAM) {
+       if (hwinfo.mode == SND_PCM_MODE_STREAM) {
                err = snd_pcm_plugin_build_stream(pcm, params->channel, &plugin);
-       } else if (sinfo.mode == SND_PCM_MODE_BLOCK) {
-               if (sinfo.flags & SND_PCM_CHNINFO_MMAP) {
+       } else if (hwinfo.mode == SND_PCM_MODE_BLOCK) {
+               if (hwinfo.flags & SND_PCM_CHNINFO_MMAP) {
                        err = snd_pcm_plugin_build_mmap(pcm, params->channel, &plugin);
                } else {
                        err = snd_pcm_plugin_build_block(pcm, params->channel, &plugin);
@@ -561,25 +596,19 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                return err;
        }
 
-       /*
-        *  ratio
-        */
-
-       ratio = snd_pcm_plugin_hardware_ratio(pcm, params->channel);
-       if (ratio <= 0)
-               return -EINVAL;
+       /* compute right sizes */
        if (params->mode == SND_PCM_MODE_STREAM) {
-               CONVERT_RATIO(sparams.buf.stream.queue_size, ratio);
-               CONVERT_RATIO(sparams.buf.stream.max_fill, ratio);
+               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);
        } else if (params->mode == SND_PCM_MODE_BLOCK) {
-               CONVERT_RATIO(sparams.buf.block.frag_size, ratio);
+               hwparams.buf.block.frag_size = snd_pcm_plugin_hardware_size(pcm, hwparams.channel, hwparams.buf.block.frag_size);
        } else {
                return -EINVAL;
        }
-       err = snd_pcm_channel_params(pcm, &sparams);
+       err = snd_pcm_channel_params(pcm, &hwparams);
        if (err < 0)
                return err;
-       err = snd_pcm_plugin_action(pcm, sparams.channel, INIT);
+       err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT);
        if (err < 0)
                return err;
        return 0;
@@ -587,7 +616,6 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
 
 int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
 {
-       double ratio;
        int err;
        
        if (!pcm || !setup || setup->channel < 0 || setup->channel > 1)
@@ -595,13 +623,10 @@ int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
        err = snd_pcm_channel_setup(pcm, setup);
        if (err < 0)
                return err;
-       ratio = snd_pcm_plugin_transfer_ratio(pcm, setup->channel);
-       if (ratio <= 0)
-               return -EINVAL;
        if (setup->mode == SND_PCM_MODE_STREAM) {
-               CONVERT_RATIO(setup->buf.stream.queue_size, ratio);
+               setup->buf.stream.queue_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.stream.queue_size);
        } else if (setup->mode == SND_PCM_MODE_BLOCK) {
-               CONVERT_RATIO(setup->buf.block.frag_size, ratio);
+               setup->buf.block.frag_size = snd_pcm_plugin_transfer_size(pcm, setup->channel, setup->buf.block.frag_size);
        } else {
                return -EINVAL;
        }
@@ -621,9 +646,9 @@ int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_channel_status_t *status)
        ratio = snd_pcm_plugin_transfer_ratio(pcm, status->channel);
        if (ratio <= 0)
                return -EINVAL;
-       CONVERT_RATIO(status->scount, ratio);
-       CONVERT_RATIO(status->count, ratio);
-       CONVERT_RATIO(status->free, ratio);
+       status->scount = snd_pcm_plugin_transfer_size(pcm, status->channel, status->scount);
+       status->count = snd_pcm_plugin_transfer_size(pcm, status->channel, status->count);
+       status->free = snd_pcm_plugin_transfer_size(pcm, status->channel, status->free);
        return 0;       
 }
 
@@ -790,7 +815,7 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
                }
                if (src_ptr1)
                        snd_pcm_plugin_alloc_unlock(pcm, src_ptr1);
-               plugin = plugin->next;
+               plugin = next;
                src_ptr = dst_ptr;
                src_ptr1 = dst_ptr1;
                dst_ptr1 = NULL;
index 0902e65638d025d85ad92deb071efb962524fec7..6fcd73716965b624fefb7941eca86c797c1a9b7b 100644 (file)
@@ -1,7 +1,7 @@
 EXTRA_LTLIBRARIES = libpcmplugin.la
 
 libpcmplugin_la_SOURCES = block.c mmap.c stream.c linear.c interleave.c \
-                         mulaw.c alaw.c adpcm.c rate.c voices.c
+                         mulaw.c alaw.c adpcm.c rate.c voices.c volbal.c
 all: libpcmplugin.la
 
 
index ef6a1dfeda6b4039894e8740da1be9e263a98c14..accc705cae23458920b68b3a17db4f4c0e85d929 100644 (file)
@@ -880,17 +880,25 @@ static ssize_t adpcm_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        }
 }
  
-int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin)
+int snd_pcm_plugin_build_adpcm(snd_pcm_format_t *src_format,
+                              snd_pcm_format_t *dst_format,
+                              snd_pcm_plugin_t **r_plugin)
 {
        struct adpcm_private_data *data;
        snd_pcm_plugin_t *plugin;
        combination_t cmd;
 
-       if (!r_plugin)
+       if (!r_plugin || !src_format || !dst_format)
                return -EINVAL;
        *r_plugin = NULL;
-       if (dst_format == SND_PCM_SFMT_IMA_ADPCM) {
-               switch (src_format) {
+
+       if (src_format->interleave != dst_format->interleave ||
+           src_format->voices != dst_format->voices ||
+           src_format->rate != dst_format->rate)
+               return -EINVAL;
+
+       if (dst_format->format == SND_PCM_SFMT_IMA_ADPCM) {
+               switch (src_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _U8_ADPCM;        break;
                case SND_PCM_SFMT_S8:           cmd = _S8_ADPCM;        break;
                case SND_PCM_SFMT_U16_LE:       cmd = _U16LE_ADPCM;     break;
@@ -900,8 +908,8 @@ int snd_pcm_plugin_build_adpcm(int src_format, int dst_format, snd_pcm_plugin_t
                default:
                        return -EINVAL;
                }
-       } else if (src_format == SND_PCM_SFMT_IMA_ADPCM) {
-               switch (dst_format) {
+       } else if (src_format->format == SND_PCM_SFMT_IMA_ADPCM) {
+               switch (dst_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _ADPCM_U8;        break;
                case SND_PCM_SFMT_S8:           cmd = _ADPCM_S8;        break;
                case SND_PCM_SFMT_U16_LE:       cmd = _ADPCM_U16LE;     break;
index 0f1ef8bb3d59f52fe5801f20cd97aafea8db9499..9dbe3ba41d68d908cf205474b180f4a8651b9a9b 100644 (file)
@@ -414,7 +414,9 @@ static ssize_t alaw_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        }
 }
  
-int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin)
+int snd_pcm_plugin_build_alaw(snd_pcm_format_t *src_format,
+                             snd_pcm_format_t *dst_format,
+                             snd_pcm_plugin_t **r_plugin)
 {
        struct alaw_private_data *data;
        snd_pcm_plugin_t *plugin;
@@ -423,8 +425,16 @@ int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t *
        if (!r_plugin)
                return -EINVAL;
        *r_plugin = NULL;
-       if (dst_format == SND_PCM_SFMT_A_LAW) {
-               switch (src_format) {
+
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+
+       if (dst_format->format == SND_PCM_SFMT_A_LAW) {
+               switch (src_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _U8_ALAW;         break;
                case SND_PCM_SFMT_S8:           cmd = _S8_ALAW;         break;
                case SND_PCM_SFMT_U16_LE:       cmd = _U16LE_ALAW;      break;
@@ -434,8 +444,8 @@ int snd_pcm_plugin_build_alaw(int src_format, int dst_format, snd_pcm_plugin_t *
                default:
                        return -EINVAL;
                }
-       } else if (src_format == SND_PCM_SFMT_A_LAW) {
-               switch (dst_format) {
+       } else if (src_format->format == SND_PCM_SFMT_A_LAW) {
+               switch (dst_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _ALAW_U8;         break;
                case SND_PCM_SFMT_S8:           cmd = _ALAW_S8;         break;
                case SND_PCM_SFMT_U16_LE:       cmd = _ALAW_U16LE;      break;
index 164beeb5f62619fb6a875791369c565ae90522a5..2038ff36a719d7b75931e63478136a176e184d52 100644 (file)
@@ -177,7 +177,9 @@ static ssize_t interleave_transfer(snd_pcm_plugin_t *plugin,
        return src_size;
 }
 
-int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int format, snd_pcm_plugin_t **r_plugin)
+int snd_pcm_plugin_build_interleave(snd_pcm_format_t *src_format,
+                                   snd_pcm_format_t *dst_format,
+                                   snd_pcm_plugin_t **r_plugin)
 {
        struct interleave_private_data *data;
        snd_pcm_plugin_t *plugin;
@@ -186,14 +188,21 @@ int snd_pcm_plugin_build_interleave(int src_interleave, int dst_interleave, int
 
        if (!r_plugin)
                return -EINVAL;
-       if (src_interleave && !dst_interleave) {
+       if (src_format->interleave && !dst_format->interleave) {
                cmd = _INTERLEAVE_NON;
-       } else if (!src_interleave && dst_interleave) {
+       } else if (!src_format->interleave && dst_format->interleave) {
                cmd = _NON_INTERLEAVE;
        } else {
                return -EINVAL;
        }
-       switch (format) {
+       if (src_format->format != dst_format->format)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+
+       switch (dst_format->format) {
        case SND_PCM_SFMT_S8:
        case SND_PCM_SFMT_U8:           size = 1; break;
        case SND_PCM_SFMT_S16_LE:
index d3b74e4ee51550b305a01960db13ba1be8926b4a..23f7dc220dd05bf6b4207b1bbaf8c9163eb1bb63 100644 (file)
@@ -310,7 +310,9 @@ static int linear_sign(int format)
        }
 }
  
-int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin)
+int snd_pcm_plugin_build_linear(snd_pcm_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
+                               snd_pcm_plugin_t **r_plugin)
 {
        struct linear_private_data *data;
        snd_pcm_plugin_t *plugin;
@@ -320,12 +322,20 @@ int snd_pcm_plugin_build_linear(int src_format, int dst_format, snd_pcm_plugin_t
        if (!r_plugin)
                return -EINVAL;
        *r_plugin = NULL;
-       wide1 = linear_wide(src_format);
-       endian1 = linear_endian(src_format);
-       sign1 = linear_sign(src_format);
-       wide2 = linear_wide(dst_format);
-       endian2 = linear_endian(dst_format);
-       sign2 = linear_sign(dst_format);
+
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+
+       wide1 = linear_wide(src_format->format);
+       endian1 = linear_endian(src_format->format);
+       sign1 = linear_sign(src_format->format);
+       wide2 = linear_wide(dst_format->format);
+       endian2 = linear_endian(dst_format->format);
+       sign2 = linear_sign(dst_format->format);
        if (wide1 < 0 || wide2 < 0 || endian1 < 0 || endian2 < 0 || sign1 < 0 || sign2 < 0)
                return -EINVAL;
 #if __BYTE_ORDER == __LITTLE_ENDIAN
index 7c04910e76d2fb7fc6a22726dbac3e3cdd684985..095752bdd3b66a52180db3a2a5547963ce0656bf 100644 (file)
@@ -424,7 +424,9 @@ static ssize_t mulaw_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        }
 }
  
-int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t **r_plugin)
+int snd_pcm_plugin_build_mulaw(snd_pcm_format_t *src_format,
+                              snd_pcm_format_t *dst_format,
+                              snd_pcm_plugin_t **r_plugin)
 {
        struct mulaw_private_data *data;
        snd_pcm_plugin_t *plugin;
@@ -433,8 +435,16 @@ int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t
        if (!r_plugin)
                return -EINVAL;
        *r_plugin = NULL;
-       if (dst_format == SND_PCM_SFMT_MU_LAW) {
-               switch (src_format) {
+
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+
+       if (dst_format->format == SND_PCM_SFMT_MU_LAW) {
+               switch (src_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _U8_MULAW;        break;
                case SND_PCM_SFMT_S8:           cmd = _S8_MULAW;        break;
                case SND_PCM_SFMT_U16_LE:       cmd = _U16LE_MULAW;     break;
@@ -444,8 +454,8 @@ int snd_pcm_plugin_build_mulaw(int src_format, int dst_format, snd_pcm_plugin_t
                default:
                        return -EINVAL;
                }
-       } else if (src_format == SND_PCM_SFMT_MU_LAW) {
-               switch (dst_format) {
+       } else if (src_format->format == SND_PCM_SFMT_MU_LAW) {
+               switch (dst_format->format) {
                case SND_PCM_SFMT_U8:           cmd = _MULAW_U8;        break;
                case SND_PCM_SFMT_S8:           cmd = _MULAW_S8;        break;
                case SND_PCM_SFMT_U16_LE:       cmd = _MULAW_U16LE;     break;
index fa1ee0a8a5bea81dda03cd350a848b236313dbf5..af849895a12fc03088607bd9f40609fafed771db 100644 (file)
@@ -48,6 +48,43 @@ struct rate_private_data {
        ssize_t old_src_size, old_dst_size;
 };
 
+static void mix_mono(struct rate_private_data *data,
+                    signed short *src_ptr, int src_size,
+                    signed short *dst_ptr, int dst_size)
+{
+       unsigned int pos;
+       signed int val;
+       signed int L_S1, L_S2;
+       
+       pos = data->pos;
+       L_S1 = data->last_L_S1;
+       L_S2 = data->last_L_S2;
+       if (pos >> SHIFT) {
+               src_ptr += ((pos >> SHIFT) - 1); pos &= MASK;
+               L_S1 = L_S2;
+               L_S2 = *src_ptr;
+       }
+       while (dst_size-- > 0) {
+               if (pos >> SHIFT) {
+                       src_ptr += (pos >> SHIFT); pos &= MASK;
+                       L_S1 = L_S2;
+                       L_S2 = *src_ptr;
+               }
+               
+               val = L_S1 + ((L_S2 - L_S1) * (signed int)pos) / BITS;
+               if (val < -32768)
+                       val = -32768;
+               else if (val > 32767)
+                       val = 32767;
+               *dst_ptr++ = val;
+               
+               pos += data->pitch;
+       }
+       data->last_L_S1 = L_S1;
+       data->last_L_S2 = L_S2;
+       data->pos = pos;
+}
+
 static void mix_stereo(struct rate_private_data *data,
                       signed short *src_ptr, int src_size,
                       signed short *dst_ptr, int dst_size)
@@ -115,7 +152,12 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
        data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin);
        if (data == NULL)
                return -EINVAL;
-       if (data->src_voices == 2) {
+       if (data->src_voices == 1) {
+               mix_mono(data, (signed short *)src_ptr, src_size / 2,
+                              (signed short *)dst_ptr, dst_size / 2);
+               return (dst_size / 2) * 2;
+       } 
+       else if (data->src_voices == 2) {
                mix_stereo(data, (signed short *)src_ptr, src_size / 4,
                                 (signed short *)dst_ptr, dst_size / 4);
                return (dst_size / 4) * 4;
@@ -152,7 +194,9 @@ static ssize_t rate_src_size(snd_pcm_plugin_t *plugin, size_t size)
        if (!plugin || size <= 0)
                return -EINVAL;
        data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin);
-       res = (((size * data->pitch) + (BITS/2)) >> SHIFT) & ~3;
+       res = (((size * data->pitch) + (BITS/2)) >> SHIFT);
+       res = res / (data->src_voices*2) * (data->src_voices*2);
+       /* Why this? */
        if (size < 128*1024) {
                if (data->old_src_size == size)
                        return data->old_dst_size;
@@ -170,7 +214,9 @@ static ssize_t rate_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        if (!plugin || size <= 0)
                return -EINVAL;
        data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin);
-       res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch) & ~3;
+       res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch);
+       res = res / (data->src_voices*2) * (data->src_voices*2);
+       /* Why this? */
        if (size < 128*1024) {
                if (data->old_dst_size == size)
                        return data->old_src_size;
@@ -180,8 +226,8 @@ static ssize_t rate_dst_size(snd_pcm_plugin_t *plugin, size_t size)
        return res;
 }
 
-int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices,
-                             int dst_format, int dst_rate, int dst_voices,
+int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format,
+                             snd_pcm_format_t *dst_format,
                              snd_pcm_plugin_t **r_plugin)
 {
        struct rate_private_data *data;
@@ -190,23 +236,33 @@ int snd_pcm_plugin_build_rate(int src_format, int src_rate, int src_voices,
        if (!r_plugin)
                return -EINVAL;
        *r_plugin = NULL;
-       if (src_voices != 2 || dst_voices != 2)
+
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (src_format->format != dst_format->format)
                return -EINVAL;
-       if (src_format != SND_PCM_SFMT_S16_LE ||
-           dst_format != SND_PCM_SFMT_S16_LE)
+       if (!dst_format->interleave)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+       if (dst_format->voices != 1 && dst_format->voices != 2)
+               return -EINVAL;
+
+       if (src_format->format != SND_PCM_SFMT_S16_LE ||
+           dst_format->format != SND_PCM_SFMT_S16_LE)
                return -EINVAL;
-       if (src_rate == dst_rate)
+       if (src_format->rate == dst_format->rate)
                return -EINVAL;
        plugin = snd_pcm_plugin_build("rate format conversion",
                                      sizeof(struct rate_private_data));
        if (plugin == NULL)
                return -ENOMEM;
        data = (struct rate_private_data *)snd_pcm_plugin_extra_data(plugin);
-       data->src_voices = src_voices;
-       data->dst_voices = dst_voices;
-       data->src_rate = src_rate;
-       data->dst_rate = dst_rate;
-       data->pitch = ((src_rate << SHIFT) + (dst_rate >> 1)) / dst_rate;
+       data->src_voices = src_format->voices;
+       data->dst_voices = dst_format->voices;
+       data->src_rate = src_format->rate;
+       data->dst_rate = dst_format->rate;
+       data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
        data->pos = 0;
        data->last_L_S1 = data->last_R_S1 = 0;
        data->last_L_S2 = data->last_R_S2 = 0;
index b5f3aedd08dc440906c6adfff58b107e69501279..be7325b49c1cd729d67111e67466a3c9f975aa98 100644 (file)
@@ -168,8 +168,8 @@ static ssize_t voices_dst_size(snd_pcm_plugin_t *plugin, size_t size)
                return (size * data->dst_voices) / data->src_voices;
 }
 
-int snd_pcm_plugin_build_voices(int src_format, int src_voices,
-                               int dst_format, int dst_voices,
+int snd_pcm_plugin_build_voices(snd_pcm_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
                                snd_pcm_plugin_t **r_plugin)
 {
        struct voices_private_data *data;
@@ -178,15 +178,22 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices,
        if (!r_plugin)
                return -EINVAL;
        *r_plugin = NULL;
-       if (src_voices == dst_voices)
+
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (!dst_format->interleave)
+               return -EINVAL;
+       if (src_format->format != dst_format->format)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
                return -EINVAL;
-       if (src_voices < 1 || src_voices > 2 ||
-           dst_voices < 1 || dst_voices > 2)
+       if (src_format->voices == dst_format->voices)
                return -EINVAL;
-       if (src_format != dst_format)
+       if (src_format->voices < 1 || src_format->voices > 2 ||
+           dst_format->voices < 1 || dst_format->voices > 2)
                return -EINVAL;
-       if (src_format < SND_PCM_SFMT_S8 || src_format > SND_PCM_SFMT_U16_BE) {
-               if (src_format != SND_PCM_SFMT_MU_LAW && src_format != SND_PCM_SFMT_A_LAW)
+       if (src_format->format < SND_PCM_SFMT_S8 || src_format->format > SND_PCM_SFMT_U16_BE) {
+               if (src_format->format != SND_PCM_SFMT_MU_LAW && src_format->format != SND_PCM_SFMT_A_LAW)
                        return -EINVAL;
        }
        plugin = snd_pcm_plugin_build("voices conversion",
@@ -194,11 +201,11 @@ int snd_pcm_plugin_build_voices(int src_format, int src_voices,
        if (plugin == NULL)
                return -ENOMEM;
        data = (struct voices_private_data *)snd_pcm_plugin_extra_data(plugin);
-       data->src_voices = src_voices;
-       data->dst_voices = dst_voices;
-       data->width = snd_pcm_format_width(src_format);
-       data->flg_merge = src_voices > dst_voices;
-       data->flg_signed = snd_pcm_format_signed(src_format);
+       data->src_voices = src_format->voices;
+       data->dst_voices = dst_format->voices;
+       data->width = snd_pcm_format_width(src_format->format);
+       data->flg_merge = src_format->voices > dst_format->voices;
+       data->flg_signed = snd_pcm_format_signed(src_format->format);
        plugin->transfer = voices_transfer;
        plugin->src_size = voices_src_size;
        plugin->dst_size = voices_dst_size;
diff --git a/src/pcm/plugin/volbal.c b/src/pcm/plugin/volbal.c
new file mode 100644 (file)
index 0000000..3747e20
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  Volume and balance Plug-In
+ *  Copyright (c) 1999 by Abramo Bagnara <abbagnara@racine.ra.it>
+ *
+ *
+ *   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 <endian.h>
+#include <byteswap.h>
+#include "../pcm_local.h"
+
+
+#define VOLBAL_RESOLUTION 16
+
+struct volbal_private_data {
+       int src_voices;
+       int noop;
+       int ttable[0];
+};
+
+static void volbal(int voices, int samples, int *ttable,
+                  signed short *src_ptr, signed short *dst_ptr)
+{
+       while (samples-- > 0) {
+               int dst_voice;
+               int *t = ttable;
+               for (dst_voice = 0; dst_voice < voices; ++dst_voice) {
+                       int v = 0;
+                       int src_voice;
+                       signed short *s = src_ptr;
+                       for (src_voice = 0; src_voice < voices; ++src_voice) {
+                               v +=  *s++ * *t++ / VOLBAL_RESOLUTION;
+                       }
+                       *dst_ptr++ = v;
+                       src_ptr += voices;
+               }
+       }
+}
+
+static ssize_t volbal_transfer(snd_pcm_plugin_t *plugin,
+                            char *src_ptr, size_t src_size,
+                            char *dst_ptr, size_t dst_size)
+{
+       struct volbal_private_data *data;
+       if (plugin == NULL || src_ptr == NULL || src_size < 0 ||
+                             dst_ptr == NULL || dst_size < 0)
+               return -EINVAL;
+       if (src_size == 0)
+               return 0;
+       data = (struct volbal_private_data *)snd_pcm_plugin_extra_data(plugin);
+       if (data == NULL)
+               return -EINVAL;
+       if (data->noop)
+               return 0;
+
+       volbal(data->src_voices, src_size / 2 / data->src_voices, data->ttable,
+              (signed short *)src_ptr, (signed short *)dst_ptr);
+       return src_size;
+}
+
+static ssize_t volbal_src_size(snd_pcm_plugin_t *plugin, size_t size)
+{
+       if (!plugin || size <= 0)
+               return -EINVAL;
+       return size;
+}
+
+static ssize_t volbal_dst_size(snd_pcm_plugin_t *plugin, size_t size)
+{
+       if (!plugin || size <= 0)
+               return -EINVAL;
+       return size;
+}
+
+
+static int volbal_load_ttable(struct volbal_private_data *data, 
+                             const int *src_ttable)
+{
+       int src_voice, dst_voice;
+       const int *sptr;
+       int *dptr;
+       data->noop = 1;
+       if (src_ttable == NULL)
+               return 0;
+       sptr = src_ttable;
+       dptr = data->ttable;
+       for (dst_voice = 0; dst_voice < data->src_voices; ++dst_voice) {
+               int t = 0;
+               for (src_voice = 0; src_voice < data->src_voices; ++src_voice) {
+                       if (*sptr < 0 || *sptr > VOLBAL_RESOLUTION)
+                               return -EINVAL;
+                       if (src_voice == dst_voice) {
+                               if (*sptr != VOLBAL_RESOLUTION)
+                                       data->noop = 0;
+                       }
+                       else {
+                               if (*sptr != 0)
+                                       data->noop = 0;
+                       }
+                       t += *sptr;
+                       *dptr++ = *sptr++;
+               }
+               if (t > VOLBAL_RESOLUTION)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+
+int snd_pcm_plugin_build_volbal(snd_pcm_format_t *src_format,
+                               snd_pcm_format_t *dst_format,
+                               int *ttable,
+                               snd_pcm_plugin_t **r_plugin)
+{
+       struct volbal_private_data *data;
+       snd_pcm_plugin_t *plugin;
+       int res;
+
+       if (!r_plugin)
+               return -EINVAL;
+       *r_plugin = NULL;
+       if (src_format->interleave != dst_format->interleave)
+               return -EINVAL;
+       if (!dst_format->interleave)
+               return -EINVAL;
+       if (src_format->format != dst_format->format)
+               return -EINVAL;
+       if (src_format->rate != dst_format->rate)
+               return -EINVAL;
+       if (src_format->voices != dst_format->voices)
+               return -EINVAL;
+
+       if (src_format->voices < 1)
+               return -EINVAL;
+       if (src_format->format != SND_PCM_SFMT_S16_LE)
+               return -EINVAL;
+       plugin = snd_pcm_plugin_build("Volume/balance conversion",
+                                     sizeof(struct volbal_private_data) + 
+                                     sizeof(data->ttable[0]) * src_format->voices * src_format->voices);
+       if (plugin == NULL)
+               return -ENOMEM;
+       data = (struct volbal_private_data *)snd_pcm_plugin_extra_data(plugin);
+       data->src_voices = src_format->voices;
+       if ((res = volbal_load_ttable(data, ttable)) < 0)
+               return res;
+
+       plugin->transfer = volbal_transfer;
+       plugin->src_size = volbal_src_size;
+       plugin->dst_size = volbal_dst_size;
+       *r_plugin = plugin;
+       return 0;
+}