]> git.alsa-project.org Git - alsa-plugins.git/commitdiff
Add livavcodec resampler plugin
authorTakashi Iwai <tiwai@suse.de>
Tue, 20 Feb 2007 11:43:58 +0000 (12:43 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 20 Feb 2007 11:43:58 +0000 (12:43 +0100)
Add a new pcm_rate plugin, "lavcrate", that uses the resampling
filter from libavcodec.  It should provide high performance and
good output quality.
Add a documentation file for lavcrate.
Update autoconf and automake to build lavcrate.

From: Nicholas Kain <njkain@gmail.com>

Makefile.am
configure.in
doc/Makefile.am
doc/lavcrate.txt [new file with mode: 0644]
rate-lavc/Makefile.am [new file with mode: 0644]
rate-lavc/gcd.h [new file with mode: 0644]
rate-lavc/rate_lavcrate.c [new file with mode: 0644]

index 06a6b5a7d134193ac38ff4dd9df722f55cb31e56..6fa2d51ecd7600c6a7d3e74cf79f89e633a3dd3e 100644 (file)
@@ -9,12 +9,13 @@ SAMPLERATEDIR = rate
 endif
 if HAVE_AVCODEC
 A52DIR = a52
+LAVCRATEDIR = rate-lavc
 endif
 if HAVE_DBUS
 MAEMODIR = maemo
 endif
 
-SUBDIRS = oss mix $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(MAEMODIR) doc
+SUBDIRS = oss mix $(JACKDIR) $(PULSEDIR) $(SAMPLERATEDIR) $(A52DIR) $(LAVCRATEDIR) $(MAEMODIR) doc
 EXTRA_DIST = hgcompile version COPYING.GPL
 AUTOMAKE_OPTIONS = foreign
 
index 62c41f689b545068fb1eb56d9408cd7f37aa9e63..2ee08da6385d091fc56cb34dfa57f4884b459337 100644 (file)
@@ -62,6 +62,7 @@ AC_OUTPUT([
        mix/Makefile
        rate/Makefile
        a52/Makefile
+       rate-lavc/Makefile
        maemo/Makefile
        doc/Makefile
 ])
index 17e5b99d5e0079615af1320603bb812d381b0912..5eda3bd3b43f8a67165bc1a79d201e412ca3633e 100644 (file)
@@ -1,2 +1,2 @@
 EXTRA_DIST = README-pcm-oss README-jack README-pulse README-maemo \
-       upmix.txt vdownmix.txt samplerate.txt a52.txt
+       upmix.txt vdownmix.txt samplerate.txt a52.txt lavcrate.txt
diff --git a/doc/lavcrate.txt b/doc/lavcrate.txt
new file mode 100644 (file)
index 0000000..faf3e25
--- /dev/null
@@ -0,0 +1,33 @@
+Rate Converter Plugin Using libavcodec
+======================================
+
+The plugin in rate-lavc subdirectory is an external rate converter using
+libavcodec's resampler.  You can use this rate converter plugin by defining a
+rate PCM with "converter" parameter, such as:
+
+       pcm.my_rate {
+               type rate
+               slave.pcm "hw"
+               converter "lavcrate"
+       }
+
+The plug plugin has also a similar field, "rate_converter".
+
+Or, more easily, define a global variable "defaults.pcm.rate_converter",
+which is used as the default converter type by plug and rate plugins:
+
+       defaults.pcm.rate_converter "lavcrate"
+
+Write the above in your ~/.asoundrc or /etc/asound.conf.
+
+The following converter types are available:
+
+  - lavcrate_higher            Use     length=64
+  - lavcrate_high              Use length=32
+  - lavcrate                   Use length=16
+  - lavcrate_fast              Use length=8
+  - lavcrate_faster            Use length=4
+
+Linear interpolation and cutoff values are automatically used depending on
+the supplied parameters and whether the plugin is used to upsample or
+downsample.
diff --git a/rate-lavc/Makefile.am b/rate-lavc/Makefile.am
new file mode 100644 (file)
index 0000000..ee0602b
--- /dev/null
@@ -0,0 +1,19 @@
+asound_module_rate_lavcrate_LTLIBRARIES = libasound_module_rate_lavcrate.la
+
+asound_module_rate_lavcratedir = $(libdir)/alsa-lib
+
+AM_CFLAGS = -Wall -g @ALSA_CFLAGS@ @AVCODEC_CFLAGS@
+AM_LDFLAGS = -module -avoid-version -export-dynamic
+
+libasound_module_rate_lavcrate_la_SOURCES = rate_lavcrate.c
+libasound_module_rate_lavcrate_la_LIBADD = @ALSA_LIBS@ @AVCODEC_LIBS@
+
+install-exec-hook:
+       rm -f $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_*.so
+       $(LN_S) libasound_module_rate_lavcrate.so $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_higher.so
+       $(LN_S) libasound_module_rate_lavcrate.so $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_high.so
+       $(LN_S) libasound_module_rate_lavcrate.so $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_fast.so
+       $(LN_S) libasound_module_rate_lavcrate.so $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_faster.so
+
+uninstall-hook:
+       rm -f $(DESTDIR)$(libdir)/alsa-lib/libasound_module_rate_lavcrate_*.so
diff --git a/rate-lavc/gcd.h b/rate-lavc/gcd.h
new file mode 100644 (file)
index 0000000..672084c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Fast implementation of greatest common divisor using the binary algorithm.
+ * Copyright (c) 2007 Nicholas Kain
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/* computes gcd using binary algorithm */
+static int gcd(int a, int b)
+{
+       int s,d;
+
+       if (!a || !b)
+               return a | b;
+
+       for (s=0; ((a|b)&1) == 0; ++s) {
+               a >>= 1;
+               b >>= 1;
+       }
+
+       while ((a&1) == 0)
+               a >>= 1;
+       
+       do {
+               while ((b&1) == 0) {
+                       b >>= 1;
+               }
+               if (a<b) {
+                       b -= a;
+               } else {
+                       d = a-b;
+                       a = b;
+                       b = d;
+               }
+               b >>= 1;
+       } while (b);
+
+       return a << s;
+}
+
diff --git a/rate-lavc/rate_lavcrate.c b/rate-lavc/rate_lavcrate.c
new file mode 100644 (file)
index 0000000..ce48495
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Rate converter plugin using libavcodec's resampler
+ * Copyright (c) 2007 by Nicholas Kain <njkain@gmail.com>
+ *
+ * based on rate converter that uses libsamplerate
+ * Copyright (c) 2006 by Takashi Iwai <tiwai@suse.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <alsa/asoundlib.h>
+#include <alsa/pcm_rate.h>
+#include <ffmpeg/avcodec.h>
+#include "gcd.h"
+
+static int filter_size = 16;
+static int phase_shift = 10; /* auto-adjusts */
+static double cutoff = 0; /* auto-adjusts */
+
+struct rate_src {
+       struct AVResampleContext *context;
+       int in_rate;
+       int out_rate;
+       int stored;
+       int point;
+       int16_t **out;
+       int16_t **in;
+       unsigned int channels;
+};
+
+static snd_pcm_uframes_t input_frames(void *obj, snd_pcm_uframes_t frames)
+{
+       return frames;
+}
+
+static snd_pcm_uframes_t output_frames(void *obj, snd_pcm_uframes_t frames)
+{
+       return frames;
+}
+
+static void pcm_src_free(void *obj)
+{
+       struct rate_src *rate = obj;
+       int i;
+
+       if (rate->out) {
+               for (i=0; i<rate->channels; i++) {
+                       free(rate->out[i]);
+               }
+               free(rate->out);
+       }
+       if (rate->in) {
+               for (i=0; i<rate->channels; i++) {
+                       free(rate->in[i]);
+               }
+               free(rate->in);
+       }
+       rate->out = rate->in = NULL;
+
+       if (rate->context) {
+               av_resample_close(rate->context);
+               rate->context = NULL;
+       }
+}
+
+static int pcm_src_init(void *obj, snd_pcm_rate_info_t *info)
+{
+       struct rate_src *rate = obj;
+       int i, ir, or;
+
+       if (! rate->context || rate->channels != info->channels) {
+               pcm_src_free(rate);
+               rate->channels = info->channels;
+               ir = rate->in_rate = info->in.rate;
+               or = rate->out_rate = info->out.rate;
+               i = gcd(or, ir);
+               if (or > ir) {
+                       phase_shift = or/i;
+               } else {
+                       phase_shift = ir/i;
+               }
+               if (cutoff <= 0.0) {
+                       cutoff = 1.0 - 1.0/filter_size;
+                       if (cutoff < 0.80)
+                               cutoff = 0.80;
+               }
+               rate->context = av_resample_init(info->out.rate, info->in.rate,
+                       filter_size, phase_shift,
+                       (info->out.rate >= info->in.rate ? 0 : 1), cutoff);
+               if (!rate->context)
+                       return -EINVAL;
+       }
+
+       rate->out = malloc(rate->channels * sizeof(int16_t *));
+       rate->in = malloc(rate->channels * sizeof(int16_t *));
+       for (i=0; i<rate->channels; i++) {
+               rate->out[i] = calloc(info->out.period_size * 2, 
+                       sizeof(int16_t));
+               rate->in[i] = calloc(info->in.period_size * 2,
+                       sizeof(int16_t));
+       }
+       rate->point = info->in.period_size / 2;
+       if (!rate->out || !rate->in) {
+               pcm_src_free(rate);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int pcm_src_adjust_pitch(void *obj, snd_pcm_rate_info_t *info)
+{
+       struct rate_src *rate = obj;
+
+       if (info->out.rate != rate->out_rate || info->in.rate != rate->in_rate)
+               pcm_src_init(obj, info);
+       return 0;
+}
+
+static void pcm_src_reset(void *obj)
+{
+       struct rate_src *rate = obj;
+       rate->stored = 0;
+}
+
+static void deinterleave(const int16_t *src, int16_t **dst, unsigned int frames,
+       unsigned int chans, int overflow)
+{
+       int i, j;
+
+       if (chans == 1) {
+               memcpy(dst + overflow, src, frames*sizeof(int16_t));
+       } else if (chans == 2) {
+               for (j=overflow; j<(frames + overflow); j++) {
+                       dst[0][j] = *(src++);
+                       dst[1][j] = *(src++);
+               }
+       } else {
+               for (j=overflow; j<(frames + overflow); j++) {
+                       for (i=0; i<chans; i++) {
+                               dst[i][j] = *(src++);
+                       }
+               }
+       }
+}
+
+static void reinterleave(int16_t **src, int16_t *dst, unsigned int frames,
+       unsigned int chans)
+{
+       int i, j;
+
+       if (chans == 1) {
+               memcpy(dst, src, frames*sizeof(int16_t));
+       } else if (chans == 2) {
+               for (j=0; j<frames; j++) {
+                       *(dst++) = src[0][j];
+                       *(dst++) = src[1][j];
+               }
+       } else {
+               for (j=0; j<frames; j++) {
+                       for (i=0; i<chans; i++) {
+                               *(dst++) = src[i][j];
+                       }
+               }
+       }
+}
+
+static void pcm_src_convert_s16(void *obj, int16_t *dst, unsigned int
+       dst_frames, const int16_t *src, unsigned int src_frames)
+{
+       struct rate_src *rate = obj;
+       int consumed = 0, chans=rate->channels, ret=0, i;
+       int total_in = rate->stored + src_frames, new_stored;
+
+       deinterleave(src, rate->in, src_frames, chans, rate->point);
+       for (i=0; i<chans; ++i) {       
+               ret = av_resample(rate->context, rate->out[i],
+                               rate->in[i]+rate->point-rate->stored, &consumed,
+                               total_in, dst_frames, i == (chans - 1));
+               new_stored = total_in-consumed;
+               memmove(rate->in[i]+rate->point-new_stored,
+                               rate->in[i]+rate->point-rate->stored+consumed,
+                               new_stored*sizeof(int16_t));
+       }
+       av_resample_compensate(rate->context,
+                       total_in-src_frames>filter_size?0:1, src_frames);
+       reinterleave(rate->out, dst, ret, chans);
+       rate->stored = total_in-consumed;
+}
+
+static void pcm_src_close(void *obj)
+{
+       pcm_src_free(obj);
+}
+
+static snd_pcm_rate_ops_t pcm_src_ops = {
+       .close = pcm_src_close,
+       .init = pcm_src_init,
+       .free = pcm_src_free,
+       .reset = pcm_src_reset,
+       .adjust_pitch = pcm_src_adjust_pitch,
+       .convert_s16 = pcm_src_convert_s16,
+       .input_frames = input_frames,
+       .output_frames = output_frames,
+};
+
+int pcm_src_open(unsigned int version, void **objp, snd_pcm_rate_ops_t *ops)
+
+{
+       struct rate_src *rate;
+
+       if (version != SND_PCM_RATE_PLUGIN_VERSION) {
+               fprintf(stderr, "Invalid rate plugin version %x\n", version);
+               return -EINVAL;
+       }
+
+       rate = calloc(1, sizeof(*rate));
+       if (!rate)
+               return -ENOMEM;
+
+       *objp = rate;
+       rate->context = NULL;
+       *ops = pcm_src_ops;
+       return 0;
+}
+
+int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate)(unsigned int version, void **objp,
+                       snd_pcm_rate_ops_t *ops)
+{
+       return pcm_src_open(version, objp, ops);
+}
+int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_higher)(unsigned int version,
+                       void **objp, snd_pcm_rate_ops_t *ops)
+{
+       filter_size = 64;
+       return pcm_src_open(version, objp, ops);
+}
+int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_high)(unsigned int version,
+                       void **objp, snd_pcm_rate_ops_t *ops)
+{
+       filter_size = 32;
+       return pcm_src_open(version, objp, ops);
+}
+int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_fast)(unsigned int version,
+                       void **objp, snd_pcm_rate_ops_t *ops)
+{
+       filter_size = 8;
+       return pcm_src_open(version, objp, ops);
+}
+int SND_PCM_RATE_PLUGIN_ENTRY(lavcrate_faster)(unsigned int version,
+                       void **objp, snd_pcm_rate_ops_t *ops)
+{
+       filter_size = 4;
+       return pcm_src_open(version, objp, ops);
+}
+
+