]> git.alsa-project.org Git - alsa-lib.git/commitdiff
- recoded the capture order plugin calls
authorJaroslav Kysela <perex@perex.cz>
Fri, 10 Dec 1999 14:43:25 +0000 (14:43 +0000)
committerJaroslav Kysela <perex@perex.cz>
Fri, 10 Dec 1999 14:43:25 +0000 (14:43 +0000)
- the rate resampling is much more smoother for "downsampling" (shrinking)

src/pcm/pcm_plugin.c
src/pcm/plugin/rate.c

index fab3415cefac1227d71f32753cb1c5fd3f1abac0..db2c67e0f9fcad4014393d04a1230171290809a4 100644 (file)
@@ -285,6 +285,13 @@ static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action)
        return 0;
 }
 
+#if 0
+#define PLUGIN_DEBUG
+#define pdprintf( args... ) printf( "plugin: " ##args)
+#else
+#define pdprintf( args... ) { ; }
+#endif
+
 int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
 {
        snd_pcm_channel_params_t hwparams;
@@ -298,6 +305,8 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                return -EINVAL;
        memcpy(&hwparams, params, sizeof(hwparams));
 
+       pdprintf("params begin\n");
+       
        /*
         *  try to decide, if a conversion is required
          */
@@ -409,6 +418,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                    dst_voices > 2)
                        dst_voices = 2;
                hwparams.format.voices = dst_voices;
+               pdprintf("params voice correction: dst_voices=%i\n", dst_voices);
        }
 
        /* rate */
@@ -417,6 +427,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                int dst_rate = params->format.rate < hwinfo.min_rate ?
                               hwinfo.min_rate : hwinfo.max_rate;
                hwparams.format.rate = dst_rate;
+               pdprintf("params rate correction: dst_rate=%i\n", dst_rate);
        }
 
        /* interleave */
@@ -448,6 +459,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
             (srcparams.format.rate != dstparams->format.rate &&
              srcparams.format.voices > 1))) {
                tmpparams.format.interleave = 1;
+               pdprintf("params interleave plugin: src=%i, dst=%i\n", srcparams.format.interleave, tmpparams.format.interleave);
                err = snd_pcm_plugin_build_interleave(&srcparams.format,
                                                      &tmpparams.format,
                                                      &plugin);
@@ -470,6 +482,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        /* voices reduction */
        if (srcparams.format.voices > dstparams->format.voices) {
                tmpparams.format.voices = dstparams->format.voices;
+               pdprintf("params voices reduction: src=%i, dst=%i\n", srcparams.format.voices, tmpparams.format.voices);
                err = snd_pcm_plugin_build_voices(&srcparams.format,
                                                  &tmpparams.format,
                                                  &plugin);
@@ -485,9 +498,31 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                srcparams.format.voices = dstparams->format.voices;
         }
 
+       /* rate resampling (when the source format has a highter resolution) */
+        if (srcparams.format.format > dstparams->format.format &&
+           srcparams.format.format <= SND_PCM_SFMT_FLOAT64_BE &&
+            srcparams.format.rate != dstparams->format.rate) {
+               tmpparams.format.rate = dstparams->format.rate;
+               pdprintf("params rate resampling (1): src=%i, dst=%i\n", srcparams.format.rate, tmpparams.format.rate);
+               err = snd_pcm_plugin_build_rate(&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.rate = dstparams->format.rate;
+        }
+
        /* format change */
        if (srcparams.format.format != dstparams->format.format) {
                tmpparams.format.format = dstparams->format.format;
+               pdprintf("params format change: src=%i, dst=%i\n", srcparams.format.format, tmpparams.format.format);
                switch (params->format.format) {
                case SND_PCM_SFMT_MU_LAW:
                        err = snd_pcm_plugin_build_mulaw(&srcparams.format,
@@ -519,9 +554,11 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
                srcparams.format.format = dstparams->format.format;
        }
 
-       /* rate resampling */
-        if (srcparams.format.rate != dstparams->format.rate) {
+       /* rate resampling (when the destonation format has an equal or highter resolution) */
+        if (srcparams.format.format <= dstparams->format.format &&
+            srcparams.format.rate != dstparams->format.rate) {
                tmpparams.format.rate = dstparams->format.rate;
+               pdprintf("params rate resampling (2): src=%i, dst=%i\n", srcparams.format.rate, tmpparams.format.rate);
                err = snd_pcm_plugin_build_rate(&srcparams.format,
                                                &tmpparams.format,
                                                &plugin);
@@ -540,6 +577,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        /* voices extension  */
        if (srcparams.format.voices < dstparams->format.voices) {
                tmpparams.format.voices = dstparams->format.voices;
+               pdprintf("params voices extension: src=%i, dst=%i\n", srcparams.format.voices, tmpparams.format.voices);
                err = snd_pcm_plugin_build_voices(&srcparams.format,
                                                  &tmpparams.format,
                                                  &plugin);
@@ -560,6 +598,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
            hwinfo.mode == SND_PCM_MODE_BLOCK &&
            srcparams.format.interleave != dstparams->format.interleave) {
                tmpparams.format.interleave = dstparams->format.interleave;
+               pdprintf("params interleave change: src=%i, dst=%i\n", srcparams.format.interleave, tmpparams.format.interleave);
                err = snd_pcm_plugin_build_interleave(&srcparams.format,
                                                      &tmpparams.format,
                                                      &plugin);
@@ -580,11 +619,14 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
         */
 
        if (hwinfo.mode == SND_PCM_MODE_STREAM) {
+               pdprintf("params stream plugin\n");
                err = snd_pcm_plugin_build_stream(pcm, params->channel, &plugin);
        } else if (hwinfo.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, &plugin);
                } else {
+                       pdprintf("params block plugin\n");
                        err = snd_pcm_plugin_build_block(pcm, params->channel, &plugin);
                }
        } else {
@@ -592,7 +634,11 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
        }
        if (err < 0)
                return err;
-       err = snd_pcm_plugin_append(pcm, params->channel, plugin);
+       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;
@@ -600,14 +646,18 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
 
        /* 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;
        }
-       // printf("requested format: format = %i, rate = %i, voices = %i\n", hwparams.format.format, hwparams.format.rate, hwparams.format.voices);
+       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;
@@ -627,9 +677,13 @@ int snd_pcm_plugin_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *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_transfer_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_transfer_size(pcm, setup->channel, setup->buf.block.frag_size);
+               pdprintf("params setup: frag_size = %i\n", setup->buf.block.frag_size);
        } else {
                return -EINVAL;
        }
@@ -677,6 +731,7 @@ 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)
                return err;
        return snd_pcm_flush_channel(pcm, channel);
@@ -811,6 +866,7 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
                        dst_ptr = src_ptr;
                        dst_size = src_size;
                }
+               pdprintf("write plugin: %s, %i, %i\n", plugin->name, src_size, dst_size);
                if ((size = plugin->transfer(plugin, src_ptr, src_size,
                                                     dst_ptr, dst_size))<0) {
                        result = size;
@@ -825,6 +881,7 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
                src_size = dst_size = size;
        }
        result = snd_pcm_plugin_transfer_size(pcm, SND_PCM_CHANNEL_PLAYBACK, size);
+       pdprintf("size = %i, result = %i, count = %i\n", size, result, count);
       __free:
        if (dst_ptr1)
                snd_pcm_plugin_alloc_unlock(pcm, dst_ptr1);
@@ -835,13 +892,13 @@ ssize_t snd_pcm_plugin_write(snd_pcm_t *pcm, const void *buffer, size_t count)
 
 ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
 {
-       snd_pcm_plugin_t *plugin, *prev;
+       snd_pcm_plugin_t *plugin, *next;
        char *dst_ptr, *dst_ptr1 = NULL, *src_ptr, *src_ptr1 = NULL;
        size_t dst_size, src_size;
        ssize_t size = 0, result = 0;
        int err;
 
-       if ((plugin = snd_pcm_plugin_last(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL)
+       if ((plugin = snd_pcm_plugin_first(pcm, SND_PCM_CHANNEL_CAPTURE)) == NULL)
                return snd_pcm_read(pcm, buffer, count);
        src_ptr = NULL;
        src_size = 0;
@@ -849,7 +906,7 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
        if (dst_size < 0)
                return dst_size;
        while (plugin) {
-               prev = plugin->prev;
+               next = plugin->next;
                if (plugin->dst_size) {
                        dst_size = plugin->dst_size(plugin, dst_size);
                        if (dst_size < 0) {
@@ -857,9 +914,9 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
                                goto __free;
                        }
                }
-               if (prev != NULL) {
-                       if (prev->transfer_src_ptr) {
-                               if ((err = prev->transfer_src_ptr(prev, &dst_ptr, &dst_size)) < 0) {
+               if (next != NULL) {
+                       if (next->transfer_src_ptr) {
+                               if ((err = next->transfer_src_ptr(next, &dst_ptr, &dst_size)) < 0) {
                                        if (dst_ptr == NULL)
                                                goto __alloc;
                                        result = err;
@@ -876,6 +933,7 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
                } else {
                        dst_ptr = buffer;
                }
+               pdprintf("read plugin: %s, %i, %i\n", plugin->name, src_size, dst_size);
                if ((size = plugin->transfer(plugin, src_ptr, src_size,
                                                     dst_ptr, dst_size))<0) {
                        result = size;
@@ -883,7 +941,7 @@ ssize_t snd_pcm_plugin_read(snd_pcm_t *pcm, void *buffer, size_t count)
                }
                if (dst_ptr1)
                        snd_pcm_plugin_alloc_unlock(pcm, dst_ptr1);
-               plugin = plugin->prev;
+               plugin = plugin->next;
                src_ptr = dst_ptr;
                src_ptr1 = dst_ptr1;
                dst_ptr1 = NULL;
index 25bea4234a2b21a1d703c847dc5a110507ec6388..4f27946f6dcf7f3cb361c7a66b9fea80dee3b87f 100644 (file)
@@ -43,6 +43,7 @@ struct rate_private_data {
        int src_rate;
        int dst_rate;
        int sample_size;
+       int expand: 1;
        unsigned int pitch;
        unsigned int pos;
        signed short last_S1[MAX_VOICES];
@@ -50,10 +51,9 @@ struct rate_private_data {
        ssize_t old_src_size, old_dst_size;
 };
 
-
-static void mix16(struct rate_private_data *data, int voices,
-                 signed short *src_ptr, int src_size,
-                 signed short *dst_ptr, int dst_size)
+static void mix16_expand(struct rate_private_data *data, int voices,
+                        signed short *src_ptr, int src_size,
+                        signed short *dst_ptr, int dst_size)
 {
        unsigned int pos;
        signed int val;
@@ -70,14 +70,13 @@ static void mix16(struct rate_private_data *data, int voices,
                dst = dst_ptr + voice;
                size = dst_size;
                if (pos >> SHIFT) {
-                       src += ((pos >> SHIFT) - 1) * voices;
                        pos &= MASK;
                        S1 = S2;
                        S2 = *src;
                }
                while (size-- > 0) {
                        if (pos >> SHIFT) {
-                               src += (pos >> SHIFT) * voices;
+                               src += voices;
                                pos &= MASK;
                                S1 = S2;
                                if ((src - src_ptr) < src_size * voices)
@@ -98,9 +97,52 @@ static void mix16(struct rate_private_data *data, int voices,
        }
 }
 
-static void mix8(struct rate_private_data *data, int voices,
-                unsigned char *src_ptr, int src_size,
-                unsigned char *dst_ptr, int dst_size)
+static void mix16_shrink(struct rate_private_data *data, int voices,
+                        signed short *src_ptr, int src_size,
+                        signed short *dst_ptr, int dst_size)
+{
+       unsigned int pos;
+       signed int val;
+       signed short S1, S2;
+       int voice;
+       signed short *src, *dst;
+       int size;
+       
+       for (voice = 0; voice < voices; ++voice) {
+               pos = data->pos;
+               S1 = data->last_S1[voice];
+               S2 = data->last_S2[voice];
+               src = src_ptr + voice;
+               dst = dst_ptr + voice;
+               size = dst_size;
+               while (size > 0) {
+                       S1 = S2;
+                       if ((src - src_ptr) < (src_size * voices)) {
+                               S2 = *src;
+                               src += voices;
+                       }
+                       if (pos >> SHIFT) {
+                               pos &= MASK;
+                               val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+                               if (val < -32768)
+                                       val = -32768;
+                               else if (val > 32767)
+                                       val = 32767;
+                               *dst = val;
+                               dst += voices;
+                               size--;
+                       }
+                       pos += data->pitch;
+               }
+               data->last_S1[voice] = S1;
+               data->last_S2[voice] = S2;
+               data->pos = pos;
+       }
+}
+
+static void mix8_expand(struct rate_private_data *data, int voices,
+                       unsigned char *src_ptr, int src_size,
+                       unsigned char *dst_ptr, int dst_size)
 {
        unsigned int pos;
        signed int val;
@@ -117,14 +159,13 @@ static void mix8(struct rate_private_data *data, int voices,
                dst = dst_ptr + voice;
                size = dst_size;
                if (pos >> SHIFT) {
-                       src += ((pos >> SHIFT) - 1) * voices;
                        pos &= MASK;
                        S1 = S2;
                        S2 = (*src << 8) ^ 0x8000;
                }
                while (size-- > 0) {
                        if (pos >> SHIFT) {
-                               src += (pos >> SHIFT) * voices;
+                               src += voices;
                                pos &= MASK;
                                S1 = S2;
                                if ((src - src_ptr) < src_size * voices)
@@ -145,6 +186,49 @@ static void mix8(struct rate_private_data *data, int voices,
        }
 }
 
+static void mix8_shrink(struct rate_private_data *data, int voices,
+                       unsigned char *src_ptr, int src_size,
+                       unsigned char *dst_ptr, int dst_size)
+{
+       unsigned int pos;
+       signed int val;
+       signed short S1, S2;
+       int voice;
+       unsigned char *src, *dst;
+       int size;
+       
+       for (voice = 0; voice < voices; ++voice) {
+               pos = data->pos;
+               S1 = data->last_S1[voice];
+               S2 = data->last_S2[voice];
+               src = src_ptr + voice;
+               dst = dst_ptr + voice;
+               size = dst_size;
+               while (size > 0) {
+                       S1 = S2;
+                       if ((src - src_ptr) < (src_size * voices)) {
+                               S2 = (*src << 8) ^ 0x8000;
+                               src += voices;
+                       }
+                       if (pos >> SHIFT) {
+                               pos &= MASK;
+                               val = S1 + ((S2 - S1) * (signed int)pos) / BITS;
+                               if (val < -32768)
+                                       val = -32768;
+                               else if (val > 32767)
+                                       val = 32767;
+                               *dst = (val >> 8) ^ 0x0080;
+                               dst += voices;
+                               size--;
+                       }
+                       pos += data->pitch;
+               }
+               data->last_S1[voice] = S1;
+               data->last_S2[voice] = S2;
+               data->pos = pos;
+       }
+}
+
 static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
                             char *src_ptr, size_t src_size,
                             char *dst_ptr, size_t dst_size)
@@ -160,13 +244,25 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
        if (data == NULL)
                return -EINVAL;
        if (data->sample_size == 2) {
-               mix16(data, data->src_voices,
-                     (signed short *)src_ptr, src_size / (data->src_voices * 2),
-                     (signed short *)dst_ptr, dst_size / (data->dst_voices * 2));
+               if (data->src_rate < data->dst_rate) {
+                       mix16_expand(data, data->src_voices,
+                                    (signed short *)src_ptr, src_size / (data->src_voices * 2),
+                                    (signed short *)dst_ptr, dst_size / (data->dst_voices * 2));
+               } else {
+                       mix16_shrink(data, data->src_voices,
+                                    (signed short *)src_ptr, src_size / (data->src_voices * 2),
+                                    (signed short *)dst_ptr, dst_size / (data->dst_voices * 2));
+               }
        } else {
-               mix8(data, data->src_voices,
-                    src_ptr, src_size / data->src_voices,
-                    dst_ptr, dst_size / data->dst_voices);
+               if (data->src_rate < data->dst_rate) {
+                       mix8_expand(data, data->src_voices,
+                                   src_ptr, src_size / data->src_voices,
+                                   dst_ptr, dst_size / data->dst_voices);
+               } else {
+                       mix8_shrink(data, data->src_voices,
+                                   src_ptr, src_size / data->src_voices,
+                                   dst_ptr, dst_size / data->dst_voices);
+               }
        }
        return dst_size / (data->sample_size * data->src_voices) * (data->sample_size * data->src_voices);
 }
@@ -201,14 +297,24 @@ 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);
+       if (data->expand) {
+               res = (((size * data->pitch) + (BITS/2)) >> SHIFT);
+       } else {
+               res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch);            
+       }
        res = res / (data->src_voices*data->sample_size) * (data->src_voices*data->sample_size);
        if (data->old_src_size > 0) {
-               if ((data->old_src_size % size) == 0) { 
-                       return (data->old_dst_size * data->old_src_size) / size;
-               } else if ((size % data->old_src_size) == 0) {
-                       return (data->old_dst_size * size) / data->old_src_size;
+               ssize_t size1 = size, res1 = data->old_dst_size;
+               while (data->old_src_size < size1) {
+                       size1 >>= 1;
+                       res1 <<= 1;
                }
+               while (data->old_src_size > size1) {
+                       size1 <<= 1;
+                       res1 >>= 1;
+               }
+               if (data->old_src_size == size1)
+                       return res1;
        }
        data->old_src_size = size;
        data->old_dst_size = res;
@@ -223,14 +329,24 @@ 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);
+       if (data->expand) {
+               res = (((size << SHIFT) + (data->pitch / 2)) / data->pitch);
+       } else {
+               res = (((size * data->pitch) + (BITS/2)) >> SHIFT);
+       }
        res = res / (data->dst_voices*data->sample_size) * (data->dst_voices*data->sample_size);
        if (data->old_dst_size > 0) {
-               if ((data->old_dst_size % size) == 0) {
-                       return (data->old_src_size * data->old_dst_size) / size;
-               } else if ((size % data->old_dst_size) == 0) {
-                       return (data->old_src_size * size) / data->old_dst_size;
+               ssize_t size1 = size, res1 = data->old_src_size;
+               while (data->old_dst_size < size1) {
+                       size1 >>= 1;
+                       res1 <<= 1;
+               }
+               while (data->old_dst_size > size1) {
+                       size1 <<= 1;
+                       res1 >>= 1;
                }
+               if (data->old_dst_size == size1)
+                       return res1;
        }
        data->old_dst_size = size;
        data->old_src_size = res;
@@ -265,7 +381,7 @@ int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format,
                return -EINVAL;
        if (src_format->rate == dst_format->rate)
                return -EINVAL;
-       plugin = snd_pcm_plugin_build("rate format conversion",
+       plugin = snd_pcm_plugin_build("rate conversion",
                                      sizeof(struct rate_private_data));
        if (plugin == NULL)
                return -ENOMEM;
@@ -275,7 +391,13 @@ int snd_pcm_plugin_build_rate(snd_pcm_format_t *src_format,
        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;
+       if (src_format->rate < dst_format->rate) {
+               data->expand = 1;
+               data->pitch = ((src_format->rate << SHIFT) + (dst_format->rate >> 1)) / dst_format->rate;
+       } else {
+               data->expand = 0;
+               data->pitch = ((dst_format->rate << SHIFT) + (src_format->rate >> 1)) / src_format->rate;
+       }
        data->pos = 0;
        for (voice = 0; voice < data->src_voices; ++voice) {
                data->last_S1[voice] = data->last_S2[voice] = 0;