From 9481014305ab8f8e1ba07749c0b7f68ec4b99e56 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 Feb 2007 12:43:58 +0100 Subject: Add livavcodec resampler plugin 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 --- rate-lavc/Makefile.am | 19 ++++ rate-lavc/gcd.h | 56 ++++++++++ rate-lavc/rate_lavcrate.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 rate-lavc/Makefile.am create mode 100644 rate-lavc/gcd.h create mode 100644 rate-lavc/rate_lavcrate.c (limited to 'rate-lavc') diff --git a/rate-lavc/Makefile.am b/rate-lavc/Makefile.am new file mode 100644 index 0000000..ee0602b --- /dev/null +++ b/rate-lavc/Makefile.am @@ -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 index 0000000..672084c --- /dev/null +++ b/rate-lavc/gcd.h @@ -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>= 1; + } while (b); + + return a << s; +} + diff --git a/rate-lavc/rate_lavcrate.c b/rate-lavc/rate_lavcrate.c new file mode 100644 index 0000000..ce48495 --- /dev/null +++ b/rate-lavc/rate_lavcrate.c @@ -0,0 +1,266 @@ +/* + * Rate converter plugin using libavcodec's resampler + * Copyright (c) 2007 by Nicholas Kain + * + * based on rate converter that uses libsamplerate + * Copyright (c) 2006 by Takashi Iwai + * + * 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 +#include +#include +#include +#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; ichannels; i++) { + free(rate->out[i]); + } + free(rate->out); + } + if (rate->in) { + for (i=0; ichannels; 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; ichannels; 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; ichannels, ret=0, i; + int total_in = rate->stored + src_frames, new_stored; + + deinterleave(src, rate->in, src_frames, chans, rate->point); + for (i=0; icontext, 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); +} + + -- cgit