Add libavresample
authorJustin Ruggles <justin.ruggles@gmail.com>
Fri, 23 Mar 2012 21:42:17 +0000 (17:42 -0400)
committerJustin Ruggles <justin.ruggles@gmail.com>
Wed, 25 Apr 2012 01:28:27 +0000 (21:28 -0400)
This is a new library for audio sample format, channel layout, and sample rate
conversion.

27 files changed:
Changelog
Makefile
configure
doc/APIchanges
libavresample/Makefile [new file with mode: 0644]
libavresample/audio_convert.c [new file with mode: 0644]
libavresample/audio_convert.h [new file with mode: 0644]
libavresample/audio_data.c [new file with mode: 0644]
libavresample/audio_data.h [new file with mode: 0644]
libavresample/audio_mix.c [new file with mode: 0644]
libavresample/audio_mix.h [new file with mode: 0644]
libavresample/audio_mix_matrix.c [new file with mode: 0644]
libavresample/avresample-test.c [new file with mode: 0644]
libavresample/avresample.h [new file with mode: 0644]
libavresample/internal.h [new file with mode: 0644]
libavresample/libavresample.v [new file with mode: 0644]
libavresample/options.c [new file with mode: 0644]
libavresample/resample.c [new file with mode: 0644]
libavresample/resample.h [new file with mode: 0644]
libavresample/utils.c [new file with mode: 0644]
libavresample/version.h [new file with mode: 0644]
libavresample/x86/Makefile [new file with mode: 0644]
libavresample/x86/audio_convert.asm [new file with mode: 0644]
libavresample/x86/audio_convert_init.c [new file with mode: 0644]
libavresample/x86/audio_mix.asm [new file with mode: 0644]
libavresample/x86/audio_mix_init.c [new file with mode: 0644]
libavutil/x86/x86util.asm

index 9e75dac..99fc1dc 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -16,6 +16,7 @@ version <next>:
 - RealAudio Lossless decoder
 - ZeroCodec decoder
 - drop support for avconv without libavfilter
+- add libavresample audio conversion library
 
 
 version 0.8:
index 2d9e435..5da4d51 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@ $(foreach VAR,$(SILENT),$(eval override $(VAR) = @$($(VAR))))
 $(eval INSTALL = @$(call ECHO,INSTALL,$$(^:$(SRC_PATH)/%=%)); $(INSTALL))
 endif
 
-ALLFFLIBS = avcodec avdevice avfilter avformat avutil swscale
+ALLFFLIBS = avcodec avdevice avfilter avformat avresample avutil swscale
 
 IFLAGS     := -I. -I$(SRC_PATH)
 CPPFLAGS   := $(IFLAGS) $(CPPFLAGS)
@@ -71,6 +71,7 @@ ALLMANPAGES = $(BASENAMES:%=%.1)
 FFLIBS-$(CONFIG_AVDEVICE) += avdevice
 FFLIBS-$(CONFIG_AVFILTER) += avfilter
 FFLIBS-$(CONFIG_AVFORMAT) += avformat
+FFLIBS-$(CONFIG_AVRESAMPLE) += avresample
 FFLIBS-$(CONFIG_AVCODEC)  += avcodec
 FFLIBS-$(CONFIG_SWSCALE)  += swscale
 
index a73d666..97db196 100755 (executable)
--- a/configure
+++ b/configure
@@ -110,6 +110,7 @@ Component options:
   --disable-avformat       disable libavformat build
   --disable-swscale        disable libswscale build
   --disable-avfilter       disable video filter support [no]
+  --disable-avresample     disable libavresample build [no]
   --disable-pthreads       disable pthreads [auto]
   --disable-w32threads     disable Win32 threads [auto]
   --enable-x11grab         enable X11 grabbing [no]
@@ -927,6 +928,7 @@ CONFIG_LIST="
     avdevice
     avfilter
     avformat
+    avresample
     avisynth
     bzlib
     dct
@@ -1536,7 +1538,7 @@ avdevice_deps="avcodec avformat"
 avformat_deps="avcodec"
 
 # programs
-avconv_deps="avcodec avfilter avformat swscale"
+avconv_deps="avcodec avfilter avformat avresample swscale"
 avplay_deps="avcodec avformat swscale sdl"
 avplay_select="rdft"
 avprobe_deps="avcodec avformat"
@@ -1684,6 +1686,7 @@ enable avcodec
 enable avdevice
 enable avfilter
 enable avformat
+enable avresample
 enable avutil
 enable swscale
 
@@ -3385,6 +3388,7 @@ get_version LIBAVCODEC  libavcodec/version.h
 get_version LIBAVDEVICE libavdevice/avdevice.h
 get_version LIBAVFILTER libavfilter/version.h
 get_version LIBAVFORMAT libavformat/version.h
+get_version LIBAVRESAMPLE libavresample/version.h
 get_version LIBAVUTIL   libavutil/avutil.h
 get_version LIBSWSCALE  libswscale/swscale.h
 
@@ -3504,4 +3508,5 @@ pkgconfig_generate libavcodec "Libav codec library" "$LIBAVCODEC_VERSION" "$extr
 pkgconfig_generate libavformat "Libav container format library" "$LIBAVFORMAT_VERSION" "$extralibs" "libavcodec = $LIBAVCODEC_VERSION"
 pkgconfig_generate libavdevice "Libav device handling library" "$LIBAVDEVICE_VERSION" "$extralibs" "libavformat = $LIBAVFORMAT_VERSION"
 pkgconfig_generate libavfilter "Libav video filtering library" "$LIBAVFILTER_VERSION" "$extralibs"
+pkgconfig_generate libavresample "Libav audio resampling library" "$LIBAVRESAMPLE_VERSION" "$extralibs"
 pkgconfig_generate libswscale "Libav image rescaling library" "$LIBSWSCALE_VERSION" "$LIBM" "libavutil = $LIBAVUTIL_VERSION"
index fed77b0..ba20c80 100644 (file)
@@ -2,16 +2,20 @@ Never assume the API of libav* to be stable unless at least 1 month has passed
 since the last major version increase.
 
 The last version increases were:
-libavcodec:  2012-01-27
-libavdevice: 2011-04-18
-libavfilter: 2011-04-18
-libavformat: 2012-01-27
-libswscale:  2011-06-20
-libavutil:   2011-04-18
+libavcodec:    2012-01-27
+libavdevice:   2011-04-18
+libavfilter:   2011-04-18
+libavformat:   2012-01-27
+libavresample: 2012-xx-xx
+libswscale:    2011-06-20
+libavutil:     2011-04-18
 
 
 API changes, most recent first:
 
+2012-xx-xx - xxxxxxx - lavr 0.0.0
+  Add libavresample audio conversion library
+
 2012-xx-xx - xxxxxxx - lavu 51.28.0 - audio_fifo.h
   Add audio FIFO functions:
     av_audio_fifo_free()
diff --git a/libavresample/Makefile b/libavresample/Makefile
new file mode 100644 (file)
index 0000000..ce3fe81
--- /dev/null
@@ -0,0 +1,15 @@
+NAME = avresample
+FFLIBS = avutil
+
+HEADERS = avresample.h                                                  \
+          version.h
+
+OBJS = audio_convert.o                                                  \
+       audio_data.o                                                     \
+       audio_mix.o                                                      \
+       audio_mix_matrix.o                                               \
+       options.o                                                        \
+       resample.o                                                       \
+       utils.o
+
+TESTPROGS = avresample
diff --git a/libavresample/audio_convert.c b/libavresample/audio_convert.c
new file mode 100644 (file)
index 0000000..200eb10
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "config.h"
+#include "libavutil/libm.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/samplefmt.h"
+#include "audio_convert.h"
+#include "audio_data.h"
+
+enum ConvFuncType {
+    CONV_FUNC_TYPE_FLAT,
+    CONV_FUNC_TYPE_INTERLEAVE,
+    CONV_FUNC_TYPE_DEINTERLEAVE,
+};
+
+typedef void (conv_func_flat)(uint8_t *out, const uint8_t *in, int len);
+
+typedef void (conv_func_interleave)(uint8_t *out, uint8_t *const *in,
+                                    int len, int channels);
+
+typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len,
+                                      int channels);
+
+struct AudioConvert {
+    AVAudioResampleContext *avr;
+    enum AVSampleFormat in_fmt;
+    enum AVSampleFormat out_fmt;
+    int channels;
+    int planes;
+    int ptr_align;
+    int samples_align;
+    int has_optimized_func;
+    const char *func_descr;
+    const char *func_descr_generic;
+    enum ConvFuncType func_type;
+    conv_func_flat         *conv_flat;
+    conv_func_flat         *conv_flat_generic;
+    conv_func_interleave   *conv_interleave;
+    conv_func_interleave   *conv_interleave_generic;
+    conv_func_deinterleave *conv_deinterleave;
+    conv_func_deinterleave *conv_deinterleave_generic;
+};
+
+void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt,
+                               enum AVSampleFormat in_fmt, int channels,
+                               int ptr_align, int samples_align,
+                               const char *descr, void *conv)
+{
+    int found = 0;
+
+    switch (ac->func_type) {
+    case CONV_FUNC_TYPE_FLAT:
+        if (av_get_packed_sample_fmt(ac->in_fmt)  == in_fmt &&
+            av_get_packed_sample_fmt(ac->out_fmt) == out_fmt) {
+            ac->conv_flat     = conv;
+            ac->func_descr    = descr;
+            ac->ptr_align     = ptr_align;
+            ac->samples_align = samples_align;
+            if (ptr_align == 1 && samples_align == 1) {
+                ac->conv_flat_generic  = conv;
+                ac->func_descr_generic = descr;
+            } else {
+                ac->has_optimized_func = 1;
+            }
+            found = 1;
+        }
+        break;
+    case CONV_FUNC_TYPE_INTERLEAVE:
+        if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt &&
+            (!channels || ac->channels == channels)) {
+            ac->conv_interleave = conv;
+            ac->func_descr      = descr;
+            ac->ptr_align       = ptr_align;
+            ac->samples_align   = samples_align;
+            if (ptr_align == 1 && samples_align == 1) {
+                ac->conv_interleave_generic = conv;
+                ac->func_descr_generic      = descr;
+            } else {
+                ac->has_optimized_func = 1;
+            }
+            found = 1;
+        }
+        break;
+    case CONV_FUNC_TYPE_DEINTERLEAVE:
+        if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt &&
+            (!channels || ac->channels == channels)) {
+            ac->conv_deinterleave = conv;
+            ac->func_descr        = descr;
+            ac->ptr_align         = ptr_align;
+            ac->samples_align     = samples_align;
+            if (ptr_align == 1 && samples_align == 1) {
+                ac->conv_deinterleave_generic = conv;
+                ac->func_descr_generic        = descr;
+            } else {
+                ac->has_optimized_func = 1;
+            }
+            found = 1;
+        }
+        break;
+    }
+    if (found) {
+        av_log(ac->avr, AV_LOG_DEBUG, "audio_convert: found function: %-4s "
+               "to %-4s (%s)\n", av_get_sample_fmt_name(ac->in_fmt),
+               av_get_sample_fmt_name(ac->out_fmt), descr);
+    }
+}
+
+#define CONV_FUNC_NAME(dst_fmt, src_fmt) conv_ ## src_fmt ## _to_ ## dst_fmt
+
+#define CONV_LOOP(otype, expr)                                              \
+    do {                                                                    \
+        *(otype *)po = expr;                                                \
+        pi += is;                                                           \
+        po += os;                                                           \
+    } while (po < end);                                                     \
+
+#define CONV_FUNC_FLAT(ofmt, otype, ifmt, itype, expr)                      \
+static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t *in,     \
+                                       int len)                             \
+{                                                                           \
+    int is       = sizeof(itype);                                           \
+    int os       = sizeof(otype);                                           \
+    const uint8_t *pi = in;                                                 \
+    uint8_t       *po = out;                                                \
+    uint8_t *end = out + os * len;                                          \
+    CONV_LOOP(otype, expr)                                                  \
+}
+
+#define CONV_FUNC_INTERLEAVE(ofmt, otype, ifmt, itype, expr)                \
+static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t **in,    \
+                                       int len, int channels)               \
+{                                                                           \
+    int ch;                                                                 \
+    int out_bps = sizeof(otype);                                            \
+    int is      = sizeof(itype);                                            \
+    int os      = channels * out_bps;                                       \
+    for (ch = 0; ch < channels; ch++) {                                     \
+        const uint8_t *pi = in[ch];                                         \
+        uint8_t       *po = out + ch * out_bps;                             \
+        uint8_t      *end = po + os * len;                                  \
+        CONV_LOOP(otype, expr)                                              \
+    }                                                                       \
+}
+
+#define CONV_FUNC_DEINTERLEAVE(ofmt, otype, ifmt, itype, expr)              \
+static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t **out, const uint8_t *in,    \
+                                       int len, int channels)               \
+{                                                                           \
+    int ch;                                                                 \
+    int in_bps = sizeof(itype);                                             \
+    int is     = channels * in_bps;                                         \
+    int os     = sizeof(otype);                                             \
+    for (ch = 0; ch < channels; ch++) {                                     \
+        const uint8_t *pi = in  + ch * in_bps;                              \
+        uint8_t       *po = out[ch];                                        \
+        uint8_t      *end = po + os * len;                                  \
+        CONV_LOOP(otype, expr)                                              \
+    }                                                                       \
+}
+
+#define CONV_FUNC_GROUP(ofmt, otype, ifmt, itype, expr) \
+CONV_FUNC_FLAT(        ofmt,      otype, ifmt,      itype, expr) \
+CONV_FUNC_INTERLEAVE(  ofmt,      otype, ifmt ## P, itype, expr) \
+CONV_FUNC_DEINTERLEAVE(ofmt ## P, otype, ifmt,      itype, expr)
+
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  uint8_t, AV_SAMPLE_FMT_U8,  uint8_t,  *(const uint8_t *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8,  uint8_t, (*(const uint8_t *)pi - 0x80) <<  8)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8,  uint8_t, (*(const uint8_t *)pi - 0x80) << 24)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float,   AV_SAMPLE_FMT_U8,  uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0f / (1 << 7)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double,  AV_SAMPLE_FMT_U8,  uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0  / (1 << 7)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  uint8_t, AV_SAMPLE_FMT_S16, int16_t, (*(const int16_t *)pi >> 8) + 0x80)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, int16_t,  *(const int16_t *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, int16_t,  *(const int16_t *)pi << 16)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float,   AV_SAMPLE_FMT_S16, int16_t,  *(const int16_t *)pi * (1.0f / (1 << 15)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double,  AV_SAMPLE_FMT_S16, int16_t,  *(const int16_t *)pi * (1.0  / (1 << 15)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  uint8_t, AV_SAMPLE_FMT_S32, int32_t, (*(const int32_t *)pi >> 24) + 0x80)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, int32_t,  *(const int32_t *)pi >> 16)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, int32_t,  *(const int32_t *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float,   AV_SAMPLE_FMT_S32, int32_t,  *(const int32_t *)pi * (1.0f / (1U << 31)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double,  AV_SAMPLE_FMT_S32, int32_t,  *(const int32_t *)pi * (1.0  / (1U << 31)))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  uint8_t, AV_SAMPLE_FMT_FLT, float,   av_clip_uint8(  lrintf(*(const float *)pi * (1  <<  7)) + 0x80))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float,   av_clip_int16(  lrintf(*(const float *)pi * (1  << 15))))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float,   av_clipl_int32(llrintf(*(const float *)pi * (1U << 31))))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float,   AV_SAMPLE_FMT_FLT, float,   *(const float *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double,  AV_SAMPLE_FMT_FLT, float,   *(const float *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  uint8_t, AV_SAMPLE_FMT_DBL, double,  av_clip_uint8(  lrint(*(const double *)pi * (1  <<  7)) + 0x80))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double,  av_clip_int16(  lrint(*(const double *)pi * (1  << 15))))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double,  av_clipl_int32(llrint(*(const double *)pi * (1U << 31))))
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float,   AV_SAMPLE_FMT_DBL, double,  *(const double *)pi)
+CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double,  AV_SAMPLE_FMT_DBL, double,  *(const double *)pi)
+
+#define SET_CONV_FUNC_GROUP(ofmt, ifmt)                                                             \
+ff_audio_convert_set_func(ac, ofmt,      ifmt,      0, 1, 1, "C", CONV_FUNC_NAME(ofmt,      ifmt)); \
+ff_audio_convert_set_func(ac, ofmt ## P, ifmt,      0, 1, 1, "C", CONV_FUNC_NAME(ofmt ## P, ifmt)); \
+ff_audio_convert_set_func(ac, ofmt,      ifmt ## P, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt,      ifmt ## P));
+
+static void set_generic_function(AudioConvert *ac)
+{
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_U8)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_U8)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_U8)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_S16)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S16)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_S32)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S32)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_FLT)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_FLT)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8,  AV_SAMPLE_FMT_DBL)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DBL)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL)
+    SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL)
+}
+
+AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
+                                     enum AVSampleFormat out_fmt,
+                                     enum AVSampleFormat in_fmt,
+                                     int channels)
+{
+    AudioConvert *ac;
+    int in_planar, out_planar;
+
+    ac = av_mallocz(sizeof(*ac));
+    if (!ac)
+        return NULL;
+
+    ac->avr      = avr;
+    ac->out_fmt  = out_fmt;
+    ac->in_fmt   = in_fmt;
+    ac->channels = channels;
+
+    in_planar  = av_sample_fmt_is_planar(in_fmt);
+    out_planar = av_sample_fmt_is_planar(out_fmt);
+
+    if (in_planar == out_planar) {
+        ac->func_type = CONV_FUNC_TYPE_FLAT;
+        ac->planes    = in_planar ? ac->channels : 1;
+    } else if (in_planar)
+        ac->func_type = CONV_FUNC_TYPE_INTERLEAVE;
+    else
+        ac->func_type = CONV_FUNC_TYPE_DEINTERLEAVE;
+
+    set_generic_function(ac);
+
+    if (ARCH_X86)
+        ff_audio_convert_init_x86(ac);
+
+    return ac;
+}
+
+int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in, int len)
+{
+    int use_generic = 1;
+
+    /* determine whether to use the optimized function based on pointer and
+       samples alignment in both the input and output */
+    if (ac->has_optimized_func) {
+        int ptr_align     = FFMIN(in->ptr_align,     out->ptr_align);
+        int samples_align = FFMIN(in->samples_align, out->samples_align);
+        int aligned_len   = FFALIGN(len, ac->samples_align);
+        if (!(ptr_align % ac->ptr_align) && samples_align >= aligned_len) {
+            len = aligned_len;
+            use_generic = 0;
+        }
+    }
+    av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (%s)\n", len,
+            av_get_sample_fmt_name(ac->in_fmt),
+            av_get_sample_fmt_name(ac->out_fmt),
+            use_generic ? ac->func_descr_generic : ac->func_descr);
+
+    switch (ac->func_type) {
+    case CONV_FUNC_TYPE_FLAT: {
+        int p;
+        if (!in->is_planar)
+            len *= in->channels;
+        if (use_generic) {
+            for (p = 0; p < ac->planes; p++)
+                ac->conv_flat_generic(out->data[p], in->data[p], len);
+        } else {
+            for (p = 0; p < ac->planes; p++)
+                ac->conv_flat(out->data[p], in->data[p], len);
+        }
+        break;
+    }
+    case CONV_FUNC_TYPE_INTERLEAVE:
+        if (use_generic)
+            ac->conv_interleave_generic(out->data[0], in->data, len, ac->channels);
+        else
+            ac->conv_interleave(out->data[0], in->data, len, ac->channels);
+        break;
+    case CONV_FUNC_TYPE_DEINTERLEAVE:
+        if (use_generic)
+            ac->conv_deinterleave_generic(out->data, in->data[0], len, ac->channels);
+        else
+            ac->conv_deinterleave(out->data, in->data[0], len, ac->channels);
+        break;
+    }
+
+    out->nb_samples = in->nb_samples;
+    return 0;
+}
diff --git a/libavresample/audio_convert.h b/libavresample/audio_convert.h
new file mode 100644 (file)
index 0000000..9227763
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_AUDIO_CONVERT_H
+#define AVRESAMPLE_AUDIO_CONVERT_H
+
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+#include "audio_data.h"
+
+typedef struct AudioConvert AudioConvert;
+
+/**
+ * Set conversion function if the parameters match.
+ *
+ * This compares the parameters of the conversion function to the parameters
+ * in the AudioConvert context. If the parameters do not match, no changes are
+ * made to the active functions. If the parameters do match and the alignment
+ * is not constrained, the function is set as the generic conversion function.
+ * If the parameters match and the alignment is constrained, the function is
+ * set as the optimized conversion function.
+ *
+ * @param ac             AudioConvert context
+ * @param out_fmt        output sample format
+ * @param in_fmt         input sample format
+ * @param channels       number of channels, or 0 for any number of channels
+ * @param ptr_align      buffer pointer alignment, in bytes
+ * @param sample_align   buffer size alignment, in samples
+ * @param descr          function type description (e.g. "C" or "SSE")
+ * @param conv           conversion function pointer
+ */
+void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt,
+                               enum AVSampleFormat in_fmt, int channels,
+                               int ptr_align, int samples_align,
+                               const char *descr, void *conv);
+
+/**
+ * Allocate and initialize AudioConvert context for sample format conversion.
+ *
+ * @param avr      AVAudioResampleContext
+ * @param out_fmt  output sample format
+ * @param in_fmt   input sample format
+ * @param channels number of channels
+ * @return         newly-allocated AudioConvert context
+ */
+AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr,
+                                     enum AVSampleFormat out_fmt,
+                                     enum AVSampleFormat in_fmt,
+                                     int channels);
+
+/**
+ * Convert audio data from one sample format to another.
+ *
+ * For each call, the alignment of the input and output AudioData buffers are
+ * examined to determine whether to use the generic or optimized conversion
+ * function (when available).
+ *
+ * @param ac     AudioConvert context
+ * @param out    output audio data
+ * @param in     input audio data
+ * @param len    number of samples to convert
+ * @return       0 on success, negative AVERROR code on failure
+ */
+int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in, int len);
+
+/* arch-specific initialization functions */
+
+void ff_audio_convert_init_x86(AudioConvert *ac);
+
+#endif /* AVRESAMPLE_AUDIO_CONVERT_H */
diff --git a/libavresample/audio_data.c b/libavresample/audio_data.c
new file mode 100644 (file)
index 0000000..3f82c50
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/mem.h"
+#include "audio_data.h"
+
+static const AVClass audio_data_class = {
+    .class_name = "AudioData",
+    .item_name  = av_default_item_name,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+/*
+ * Calculate alignment for data pointers.
+ */
+static void calc_ptr_alignment(AudioData *a)
+{
+    int p;
+    int min_align = 128;
+
+    for (p = 0; p < a->planes; p++) {
+        int cur_align = 128;
+        while ((intptr_t)a->data[p] % cur_align)
+            cur_align >>= 1;
+        if (cur_align < min_align)
+            min_align = cur_align;
+    }
+    a->ptr_align = min_align;
+}
+
+int ff_audio_data_set_channels(AudioData *a, int channels)
+{
+    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS ||
+        channels > a->allocated_channels)
+        return AVERROR(EINVAL);
+
+    a->channels  = channels;
+    a->planes    = a->is_planar ? channels : 1;
+
+    calc_ptr_alignment(a);
+
+    return 0;
+}
+
+int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels,
+                       int nb_samples, enum AVSampleFormat sample_fmt,
+                       int read_only, const char *name)
+{
+    int p;
+
+    memset(a, 0, sizeof(*a));
+    a->class = &audio_data_class;
+
+    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels);
+        return AVERROR(EINVAL);
+    }
+
+    a->sample_size = av_get_bytes_per_sample(sample_fmt);
+    if (!a->sample_size) {
+        av_log(a, AV_LOG_ERROR, "invalid sample format\n");
+        return AVERROR(EINVAL);
+    }
+    a->is_planar = av_sample_fmt_is_planar(sample_fmt);
+    a->planes    = a->is_planar ? channels : 1;
+    a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
+
+    for (p = 0; p < (a->is_planar ? channels : 1); p++) {
+        if (!src[p]) {
+            av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p);
+            return AVERROR(EINVAL);
+        }
+        a->data[p] = src[p];
+    }
+    a->allocated_samples  = nb_samples * !read_only;
+    a->nb_samples         = nb_samples;
+    a->sample_fmt         = sample_fmt;
+    a->channels           = channels;
+    a->allocated_channels = channels;
+    a->read_only          = read_only;
+    a->allow_realloc      = 0;
+    a->name               = name ? name : "{no name}";
+
+    calc_ptr_alignment(a);
+    a->samples_align = plane_size / a->stride;
+
+    return 0;
+}
+
+AudioData *ff_audio_data_alloc(int channels, int nb_samples,
+                               enum AVSampleFormat sample_fmt, const char *name)
+{
+    AudioData *a;
+    int ret;
+
+    if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS)
+        return NULL;
+
+    a = av_mallocz(sizeof(*a));
+    if (!a)
+        return NULL;
+
+    a->sample_size = av_get_bytes_per_sample(sample_fmt);
+    if (!a->sample_size) {
+        av_free(a);
+        return NULL;
+    }
+    a->is_planar = av_sample_fmt_is_planar(sample_fmt);
+    a->planes    = a->is_planar ? channels : 1;
+    a->stride    = a->sample_size * (a->is_planar ? 1 : channels);
+
+    a->class              = &audio_data_class;
+    a->sample_fmt         = sample_fmt;
+    a->channels           = channels;
+    a->allocated_channels = channels;
+    a->read_only          = 0;
+    a->allow_realloc      = 1;
+    a->name               = name ? name : "{no name}";
+
+    if (nb_samples > 0) {
+        ret = ff_audio_data_realloc(a, nb_samples);
+        if (ret < 0) {
+            av_free(a);
+            return NULL;
+        }
+        return a;
+    } else {
+        calc_ptr_alignment(a);
+        return a;
+    }
+}
+
+int ff_audio_data_realloc(AudioData *a, int nb_samples)
+{
+    int ret, new_buf_size, plane_size, p;
+
+    /* check if buffer is already large enough */
+    if (a->allocated_samples >= nb_samples)
+        return 0;
+
+    /* validate that the output is not read-only and realloc is allowed */
+    if (a->read_only || !a->allow_realloc)
+        return AVERROR(EINVAL);
+
+    new_buf_size = av_samples_get_buffer_size(&plane_size,
+                                              a->allocated_channels, nb_samples,
+                                              a->sample_fmt, 0);
+    if (new_buf_size < 0)
+        return new_buf_size;
+
+    /* if there is already data in the buffer and the sample format is planar,
+       allocate a new buffer and copy the data, otherwise just realloc the
+       internal buffer and set new data pointers */
+    if (a->nb_samples > 0 && a->is_planar) {
+        uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL };
+
+        ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels,
+                               nb_samples, a->sample_fmt, 0);
+        if (ret < 0)
+            return ret;
+
+        for (p = 0; p < a->planes; p++)
+            memcpy(new_data[p], a->data[p], a->nb_samples * a->stride);
+
+        av_freep(&a->buffer);
+        memcpy(a->data, new_data, sizeof(new_data));
+        a->buffer = a->data[0];
+    } else {
+        av_freep(&a->buffer);
+        a->buffer = av_malloc(new_buf_size);
+        if (!a->buffer)
+            return AVERROR(ENOMEM);
+        ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer,
+                                     a->allocated_channels, nb_samples,
+                                     a->sample_fmt, 0);
+        if (ret < 0)
+            return ret;
+    }
+    a->buffer_size       = new_buf_size;
+    a->allocated_samples = nb_samples;
+
+    calc_ptr_alignment(a);
+    a->samples_align = plane_size / a->stride;
+
+    return 0;
+}
+
+void ff_audio_data_free(AudioData **a)
+{
+    if (!*a)
+        return;
+    av_free((*a)->buffer);
+    av_freep(a);
+}
+
+int ff_audio_data_copy(AudioData *dst, AudioData *src)
+{
+    int ret, p;
+
+    /* validate input/output compatibility */
+    if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels)
+        return AVERROR(EINVAL);
+
+    /* if the input is empty, just empty the output */
+    if (!src->nb_samples) {
+        dst->nb_samples = 0;
+        return 0;
+    }
+
+    /* reallocate output if necessary */
+    ret = ff_audio_data_realloc(dst, src->nb_samples);
+    if (ret < 0)
+        return ret;
+
+    /* copy data */
+    for (p = 0; p < src->planes; p++)
+        memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride);
+    dst->nb_samples = src->nb_samples;
+
+    return 0;
+}
+
+int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src,
+                          int src_offset, int nb_samples)
+{
+    int ret, p, dst_offset2, dst_move_size;
+
+    /* validate input/output compatibility */
+    if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) {
+        av_log(src, AV_LOG_ERROR, "sample format mismatch\n");
+        return AVERROR(EINVAL);
+    }
+
+    /* validate offsets are within the buffer bounds */
+    if (dst_offset < 0 || dst_offset > dst->nb_samples ||
+        src_offset < 0 || src_offset > src->nb_samples) {
+        av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n",
+               src_offset, dst_offset);
+        return AVERROR(EINVAL);
+    }
+
+    /* check offsets and sizes to see if we can just do nothing and return */
+    if (nb_samples > src->nb_samples - src_offset)
+        nb_samples = src->nb_samples - src_offset;
+    if (nb_samples <= 0)
+        return 0;
+
+    /* validate that the output is not read-only */
+    if (dst->read_only) {
+        av_log(dst, AV_LOG_ERROR, "dst is read-only\n");
+        return AVERROR(EINVAL);
+    }
+
+    /* reallocate output if necessary */
+    ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples);
+    if (ret < 0) {
+        av_log(dst, AV_LOG_ERROR, "error reallocating dst\n");
+        return ret;
+    }
+
+    dst_offset2   = dst_offset + nb_samples;
+    dst_move_size = dst->nb_samples - dst_offset;
+
+    for (p = 0; p < src->planes; p++) {
+        if (dst_move_size > 0) {
+            memmove(dst->data[p] + dst_offset2 * dst->stride,
+                    dst->data[p] + dst_offset  * dst->stride,
+                    dst_move_size * dst->stride);
+        }
+        memcpy(dst->data[p] + dst_offset * dst->stride,
+               src->data[p] + src_offset * src->stride,
+               nb_samples * src->stride);
+    }
+    dst->nb_samples += nb_samples;
+
+    return 0;
+}
+
+void ff_audio_data_drain(AudioData *a, int nb_samples)
+{
+    if (a->nb_samples <= nb_samples) {
+        /* drain the whole buffer */
+        a->nb_samples = 0;
+    } else {
+        int p;
+        int move_offset = a->stride * nb_samples;
+        int move_size   = a->stride * (a->nb_samples - nb_samples);
+
+        for (p = 0; p < a->planes; p++)
+            memmove(a->data[p], a->data[p] + move_offset, move_size);
+
+        a->nb_samples -= nb_samples;
+    }
+}
+
+int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset,
+                              int nb_samples)
+{
+    uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS];
+    int offset_size, p;
+
+    if (offset >= a->nb_samples)
+        return 0;
+    offset_size = offset * a->stride;
+    for (p = 0; p < a->planes; p++)
+        offset_data[p] = a->data[p] + offset_size;
+
+    return av_audio_fifo_write(af, (void **)offset_data, nb_samples);
+}
+
+int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples)
+{
+    int ret;
+
+    if (a->read_only)
+        return AVERROR(EINVAL);
+
+    ret = ff_audio_data_realloc(a, nb_samples);
+    if (ret < 0)
+        return ret;
+
+    ret = av_audio_fifo_read(af, (void **)a->data, nb_samples);
+    if (ret >= 0)
+        a->nb_samples = ret;
+    return ret;
+}
diff --git a/libavresample/audio_data.h b/libavresample/audio_data.h
new file mode 100644 (file)
index 0000000..4609ebc
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_AUDIO_DATA_H
+#define AVRESAMPLE_AUDIO_DATA_H
+
+#include <stdint.h>
+
+#include "libavutil/audio_fifo.h"
+#include "libavutil/log.h"
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+
+/**
+ * Audio buffer used for intermediate storage between conversion phases.
+ */
+typedef struct AudioData {
+    const AVClass *class;               /**< AVClass for logging            */
+    uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; /**< data plane pointers        */
+    uint8_t *buffer;                    /**< data buffer                    */
+    unsigned int buffer_size;           /**< allocated buffer size          */
+    int allocated_samples;              /**< number of samples the buffer can hold */
+    int nb_samples;                     /**< current number of samples      */
+    enum AVSampleFormat sample_fmt;     /**< sample format                  */
+    int channels;                       /**< channel count                  */
+    int allocated_channels;             /**< allocated channel count        */
+    int is_planar;                      /**< sample format is planar        */
+    int planes;                         /**< number of data planes          */
+    int sample_size;                    /**< bytes per sample               */
+    int stride;                         /**< sample byte offset within a plane */
+    int read_only;                      /**< data is read-only              */
+    int allow_realloc;                  /**< realloc is allowed             */
+    int ptr_align;                      /**< minimum data pointer alignment */
+    int samples_align;                  /**< allocated samples alignment    */
+    const char *name;                   /**< name for debug logging         */
+} AudioData;
+
+int ff_audio_data_set_channels(AudioData *a, int channels);
+
+/**
+ * Initialize AudioData using a given source.
+ *
+ * This does not allocate an internal buffer. It only sets the data pointers
+ * and audio parameters.
+ *
+ * @param a               AudioData struct
+ * @param src             source data pointers
+ * @param plane_size      plane size, in bytes.
+ *                        This can be 0 if unknown, but that will lead to
+ *                        optimized functions not being used in many cases,
+ *                        which could slow down some conversions.
+ * @param channels        channel count
+ * @param nb_samples      number of samples in the source data
+ * @param sample_fmt      sample format
+ * @param read_only       indicates if buffer is read only or read/write
+ * @param name            name for debug logging (can be NULL)
+ * @return                0 on success, negative AVERROR value on error
+ */
+int ff_audio_data_init(AudioData *a, void **src, int plane_size, int channels,
+                       int nb_samples, enum AVSampleFormat sample_fmt,
+                       int read_only, const char *name);
+
+/**
+ * Allocate AudioData.
+ *
+ * This allocates an internal buffer and sets audio parameters.
+ *
+ * @param channels        channel count
+ * @param nb_samples      number of samples to allocate space for
+ * @param sample_fmt      sample format
+ * @param name            name for debug logging (can be NULL)
+ * @return                newly allocated AudioData struct, or NULL on error
+ */
+AudioData *ff_audio_data_alloc(int channels, int nb_samples,
+                               enum AVSampleFormat sample_fmt,
+                               const char *name);
+
+/**
+ * Reallocate AudioData.
+ *
+ * The AudioData must have been previously allocated with ff_audio_data_alloc().
+ *
+ * @param a           AudioData struct
+ * @param nb_samples  number of samples to allocate space for
+ * @return            0 on success, negative AVERROR value on error
+ */
+int ff_audio_data_realloc(AudioData *a, int nb_samples);
+
+/**
+ * Free AudioData.
+ *
+ * The AudioData must have been previously allocated with ff_audio_data_alloc().
+ *
+ * @param a  AudioData struct
+ */
+void ff_audio_data_free(AudioData **a);
+
+/**
+ * Copy data from one AudioData to another.
+ *
+ * @param out  output AudioData
+ * @param in   input AudioData
+ * @return     0 on success, negative AVERROR value on error
+ */
+int ff_audio_data_copy(AudioData *out, AudioData *in);
+
+/**
+ * Append data from one AudioData to the end of another.
+ *
+ * @param dst         destination AudioData
+ * @param dst_offset  offset, in samples, to start writing, relative to the
+ *                    start of dst
+ * @param src         source AudioData
+ * @param src_offset  offset, in samples, to start copying, relative to the
+ *                    start of the src
+ * @param nb_samples  number of samples to copy
+ * @return            0 on success, negative AVERROR value on error
+ */
+int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src,
+                          int src_offset, int nb_samples);
+
+/**
+ * Drain samples from the start of the AudioData.
+ *
+ * Remaining samples are shifted to the start of the AudioData.
+ *
+ * @param a           AudioData struct
+ * @param nb_samples  number of samples to drain
+ */
+void ff_audio_data_drain(AudioData *a, int nb_samples);
+
+/**
+ * Add samples in AudioData to an AVAudioFifo.
+ *
+ * @param af          Audio FIFO Buffer
+ * @param a           AudioData struct
+ * @param offset      number of samples to skip from the start of the data
+ * @param nb_samples  number of samples to add to the FIFO
+ * @return            number of samples actually added to the FIFO, or
+ *                    negative AVERROR code on error
+ */
+int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset,
+                              int nb_samples);
+
+/**
+ * Read samples from an AVAudioFifo to AudioData.
+ *
+ * @param af          Audio FIFO Buffer
+ * @param a           AudioData struct
+ * @param nb_samples  number of samples to read from the FIFO
+ * @return            number of samples actually read from the FIFO, or
+ *                    negative AVERROR code on error
+ */
+int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples);
+
+#endif /* AVRESAMPLE_AUDIO_DATA_H */
diff --git a/libavresample/audio_mix.c b/libavresample/audio_mix.c
new file mode 100644 (file)
index 0000000..34252bf
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/libm.h"
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+#include "internal.h"
+#include "audio_data.h"
+#include "audio_mix.h"
+
+static const char *coeff_type_names[] = { "q6", "q15", "flt" };
+
+void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt,
+                           enum AVMixCoeffType coeff_type, int in_channels,
+                           int out_channels, int ptr_align, int samples_align,
+                           const char *descr, void *mix_func)
+{
+    if (fmt == am->fmt && coeff_type == am->coeff_type &&
+        ( in_channels ==  am->in_channels ||  in_channels == 0) &&
+        (out_channels == am->out_channels || out_channels == 0)) {
+        char chan_str[16];
+        am->mix           = mix_func;
+        am->func_descr    = descr;
+        am->ptr_align     = ptr_align;
+        am->samples_align = samples_align;
+        if (ptr_align == 1 && samples_align == 1) {
+            am->mix_generic        = mix_func;
+            am->func_descr_generic = descr;
+        } else {
+            am->has_optimized_func = 1;
+        }
+        if (in_channels) {
+            if (out_channels)
+                snprintf(chan_str, sizeof(chan_str), "[%d to %d] ",
+                         in_channels, out_channels);
+            else
+                snprintf(chan_str, sizeof(chan_str), "[%d to any] ",
+                         in_channels);
+        } else if (out_channels) {
+                snprintf(chan_str, sizeof(chan_str), "[any to %d] ",
+                         out_channels);
+        }
+        av_log(am->avr, AV_LOG_DEBUG, "audio_mix: found function: [fmt=%s] "
+               "[c=%s] %s(%s)\n", av_get_sample_fmt_name(fmt),
+               coeff_type_names[coeff_type],
+               (in_channels || out_channels) ? chan_str : "", descr);
+    }
+}
+
+#define MIX_FUNC_NAME(fmt, cfmt) mix_any_ ## fmt ##_## cfmt ##_c
+
+#define MIX_FUNC_GENERIC(fmt, cfmt, stype, ctype, sumtype, expr)            \
+static void MIX_FUNC_NAME(fmt, cfmt)(stype **samples, ctype **matrix,       \
+                                     int len, int out_ch, int in_ch)        \
+{                                                                           \
+    int i, in, out;                                                         \
+    stype temp[AVRESAMPLE_MAX_CHANNELS];                                    \
+    for (i = 0; i < len; i++) {                                             \
+        for (out = 0; out < out_ch; out++) {                                \
+            sumtype sum = 0;                                                \
+            for (in = 0; in < in_ch; in++)                                  \
+                sum += samples[in][i] * matrix[out][in];                    \
+            temp[out] = expr;                                               \
+        }                                                                   \
+        for (out = 0; out < out_ch; out++)                                  \
+            samples[out][i] = temp[out];                                    \
+    }                                                                       \
+}
+
+MIX_FUNC_GENERIC(FLTP, FLT, float,   float,   float,   sum)
+MIX_FUNC_GENERIC(S16P, FLT, int16_t, float,   float,   av_clip_int16(lrintf(sum)))
+MIX_FUNC_GENERIC(S16P, Q15, int16_t, int32_t, int64_t, av_clip_int16(sum >> 15))
+MIX_FUNC_GENERIC(S16P, Q6,  int16_t, int16_t, int32_t, av_clip_int16(sum >> 6))
+
+/* TODO: templatize the channel-specific C functions */
+
+static void mix_2_to_1_fltp_flt_c(float **samples, float **matrix, int len,
+                                  int out_ch, int in_ch)
+{
+    float *src0 = samples[0];
+    float *src1 = samples[1];
+    float *dst  = src0;
+    float m0    = matrix[0][0];
+    float m1    = matrix[0][1];
+
+    while (len > 4) {
+        *dst++ = *src0++ * m0 + *src1++ * m1;
+        *dst++ = *src0++ * m0 + *src1++ * m1;
+        *dst++ = *src0++ * m0 + *src1++ * m1;
+        *dst++ = *src0++ * m0 + *src1++ * m1;
+        len -= 4;
+    }
+    while (len > 0) {
+        *dst++ = *src0++ * m0 + *src1++ * m1;
+        len--;
+    }
+}
+
+static void mix_1_to_2_fltp_flt_c(float **samples, float **matrix, int len,
+                                  int out_ch, int in_ch)
+{
+    float v;
+    float *dst0 = samples[0];
+    float *dst1 = samples[1];
+    float *src  = dst0;
+    float m0    = matrix[0][0];
+    float m1    = matrix[1][0];
+
+    while (len > 4) {
+        v = *src++;
+        *dst0++ = v * m1;
+        *dst1++ = v * m0;
+        v = *src++;
+        *dst0++ = v * m1;
+        *dst1++ = v * m0;
+        v = *src++;
+        *dst0++ = v * m1;
+        *dst1++ = v * m0;
+        v = *src++;
+        *dst0++ = v * m1;
+        *dst1++ = v * m0;
+        len -= 4;
+    }
+    while (len > 0) {
+        v = *src++;
+        *dst0++ = v * m1;
+        *dst1++ = v * m0;
+        len--;
+    }
+}
+
+static void mix_6_to_2_fltp_flt_c(float **samples, float **matrix, int len,
+                                  int out_ch, int in_ch)
+{
+    float v0, v1;
+    float *src0 = samples[0];
+    float *src1 = samples[1];
+    float *src2 = samples[2];
+    float *src3 = samples[3];
+    float *src4 = samples[4];
+    float *src5 = samples[5];
+    float *dst0 = src0;
+    float *dst1 = src1;
+    float *m0   = matrix[0];
+    float *m1   = matrix[1];
+
+    while (len > 0) {
+        v0 = *src0++;
+        v1 = *src1++;
+        *dst0++ = v0      * m0[0] +
+                  v1      * m0[1] +
+                  *src2   * m0[2] +
+                  *src3   * m0[3] +
+                  *src4   * m0[4] +
+                  *src5   * m0[5];
+        *dst1++ = v0      * m1[0] +
+                  v1      * m1[1] +
+                  *src2++ * m1[2] +
+                  *src3++ * m1[3] +
+                  *src4++ * m1[4] +
+                  *src5++ * m1[5];
+        len--;
+    }
+}
+
+static void mix_2_to_6_fltp_flt_c(float **samples, float **matrix, int len,
+                                  int out_ch, int in_ch)
+{
+    float v0, v1;
+    float *dst0 = samples[0];
+    float *dst1 = samples[1];
+    float *dst2 = samples[2];
+    float *dst3 = samples[3];
+    float *dst4 = samples[4];
+    float *dst5 = samples[5];
+    float *src0 = dst0;
+    float *src1 = dst1;
+
+    while (len > 0) {
+        v0 = *src0++;
+        v1 = *src1++;
+        *dst0++ = v0 * matrix[0][0] + v1 * matrix[0][1];
+        *dst1++ = v0 * matrix[1][0] + v1 * matrix[1][1];
+        *dst2++ = v0 * matrix[2][0] + v1 * matrix[2][1];
+        *dst3++ = v0 * matrix[3][0] + v1 * matrix[3][1];
+        *dst4++ = v0 * matrix[4][0] + v1 * matrix[4][1];
+        *dst5++ = v0 * matrix[5][0] + v1 * matrix[5][1];
+        len--;
+    }
+}
+
+static int mix_function_init(AudioMix *am)
+{
+    /* any-to-any C versions */
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                          0, 0, 1, 1, "C", MIX_FUNC_NAME(FLTP, FLT));
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,
+                          0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, FLT));
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q15,
+                          0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q15));
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q6,
+                          0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q6));
+
+    /* channel-specific C versions */
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                          2, 1, 1, 1, "C", mix_2_to_1_fltp_flt_c);
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                          1, 2, 1, 1, "C", mix_1_to_2_fltp_flt_c);
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                          6, 2, 1, 1, "C", mix_6_to_2_fltp_flt_c);
+
+    ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                          2, 6, 1, 1, "C", mix_2_to_6_fltp_flt_c);
+
+    if (ARCH_X86)
+        ff_audio_mix_init_x86(am);
+
+    if (!am->mix) {
+        av_log(am->avr, AV_LOG_ERROR, "audio_mix: NO FUNCTION FOUND: [fmt=%s] "
+               "[c=%s] [%d to %d]\n", av_get_sample_fmt_name(am->fmt),
+               coeff_type_names[am->coeff_type], am->in_channels,
+               am->out_channels);
+        return AVERROR_PATCHWELCOME;
+    }
+    return 0;
+}
+
+int ff_audio_mix_init(AVAudioResampleContext *avr)
+{
+    int ret;
+
+    /* build matrix if the user did not already set one */
+    if (!avr->am->matrix) {
+        int i, j;
+        char in_layout_name[128];
+        char out_layout_name[128];
+        double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels *
+                                        sizeof(*matrix_dbl));
+        if (!matrix_dbl)
+            return AVERROR(ENOMEM);
+
+        ret = avresample_build_matrix(avr->in_channel_layout,
+                                      avr->out_channel_layout,
+                                      avr->center_mix_level,
+                                      avr->surround_mix_level,
+                                      avr->lfe_mix_level, 1, matrix_dbl,
+                                      avr->in_channels);
+        if (ret < 0) {
+            av_free(matrix_dbl);
+            return ret;
+        }
+
+        av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name),
+                                     avr->in_channels, avr->in_channel_layout);
+        av_get_channel_layout_string(out_layout_name, sizeof(out_layout_name),
+                                     avr->out_channels, avr->out_channel_layout);
+        av_log(avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n",
+               in_layout_name, out_layout_name);
+        for (i = 0; i < avr->out_channels; i++) {
+            for (j = 0; j < avr->in_channels; j++) {
+                av_log(avr, AV_LOG_DEBUG, "  %0.3f ",
+                       matrix_dbl[i * avr->in_channels + j]);
+            }
+            av_log(avr, AV_LOG_DEBUG, "\n");
+        }
+
+        ret = avresample_set_matrix(avr, matrix_dbl, avr->in_channels);
+        if (ret < 0) {
+            av_free(matrix_dbl);
+            return ret;
+        }
+        av_free(matrix_dbl);
+    }
+
+    avr->am->fmt          = avr->internal_sample_fmt;
+    avr->am->coeff_type   = avr->mix_coeff_type;
+    avr->am->in_layout    = avr->in_channel_layout;
+    avr->am->out_layout   = avr->out_channel_layout;
+    avr->am->in_channels  = avr->in_channels;
+    avr->am->out_channels = avr->out_channels;
+
+    ret = mix_function_init(avr->am);
+    if (ret < 0)
+        return ret;
+
+    return 0;
+}
+
+void ff_audio_mix_close(AudioMix *am)
+{
+    if (!am)
+        return;
+    if (am->matrix) {
+        av_free(am->matrix[0]);
+        am->matrix = NULL;
+    }
+    memset(am->matrix_q6,  0, sizeof(am->matrix_q6 ));
+    memset(am->matrix_q15, 0, sizeof(am->matrix_q15));
+    memset(am->matrix_flt, 0, sizeof(am->matrix_flt));
+}
+
+int ff_audio_mix(AudioMix *am, AudioData *src)
+{
+    int use_generic = 1;
+    int len = src->nb_samples;
+
+    /* determine whether to use the optimized function based on pointer and
+       samples alignment in both the input and output */
+    if (am->has_optimized_func) {
+        int aligned_len = FFALIGN(len, am->samples_align);
+        if (!(src->ptr_align % am->ptr_align) &&
+            src->samples_align >= aligned_len) {
+            len = aligned_len;
+            use_generic = 0;
+        }
+    }
+    av_dlog(am->avr, "audio_mix: %d samples - %d to %d channels (%s)\n",
+            src->nb_samples, am->in_channels, am->out_channels,
+            use_generic ? am->func_descr_generic : am->func_descr);
+
+    if (use_generic)
+        am->mix_generic(src->data, am->matrix, len, am->out_channels,
+                        am->in_channels);
+    else
+        am->mix(src->data, am->matrix, len, am->out_channels, am->in_channels);
+
+    ff_audio_data_set_channels(src, am->out_channels);
+
+    return 0;
+}
diff --git a/libavresample/audio_mix.h b/libavresample/audio_mix.h
new file mode 100644 (file)
index 0000000..ffa1b23
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_AUDIO_MIX_H
+#define AVRESAMPLE_AUDIO_MIX_H
+
+#include <stdint.h>
+
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+#include "audio_data.h"
+
+typedef void (mix_func)(uint8_t **src, void **matrix, int len, int out_ch,
+                        int in_ch);
+
+typedef struct AudioMix {
+    AVAudioResampleContext *avr;
+    enum AVSampleFormat fmt;
+    enum AVMixCoeffType coeff_type;
+    uint64_t in_layout;
+    uint64_t out_layout;
+    int in_channels;
+    int out_channels;
+
+    int ptr_align;
+    int samples_align;
+    int has_optimized_func;
+    const char *func_descr;
+    const char *func_descr_generic;
+    mix_func *mix;
+    mix_func *mix_generic;
+
+    int16_t *matrix_q6[AVRESAMPLE_MAX_CHANNELS];
+    int32_t *matrix_q15[AVRESAMPLE_MAX_CHANNELS];
+    float   *matrix_flt[AVRESAMPLE_MAX_CHANNELS];
+    void   **matrix;
+} AudioMix;
+
+/**
+ * Set mixing function if the parameters match.
+ *
+ * This compares the parameters of the mixing function to the parameters in the
+ * AudioMix context. If the parameters do not match, no changes are made to the
+ * active functions. If the parameters do match and the alignment is not
+ * constrained, the function is set as the generic mixing function. If the
+ * parameters match and the alignment is constrained, the function is set as
+ * the optimized mixing function.
+ *
+ * @param am             AudioMix context
+ * @param fmt            input/output sample format
+ * @param coeff_type     mixing coefficient type
+ * @param in_channels    number of input channels, or 0 for any number of channels
+ * @param out_channels   number of output channels, or 0 for any number of channels
+ * @param ptr_align      buffer pointer alignment, in bytes
+ * @param sample_align   buffer size alignment, in samples
+ * @param descr          function type description (e.g. "C" or "SSE")
+ * @param mix_func       mixing function pointer
+ */
+void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt,
+                           enum AVMixCoeffType coeff_type, int in_channels,
+                           int out_channels, int ptr_align, int samples_align,
+                           const char *descr, void *mix_func);
+
+/**
+ * Initialize the AudioMix context in the AVAudioResampleContext.
+ *
+ * The parameters in the AVAudioResampleContext are used to initialize the
+ * AudioMix context and set the mixing matrix.
+ *
+ * @param avr  AVAudioResampleContext
+ * @return     0 on success, negative AVERROR code on failure
+ */
+int ff_audio_mix_init(AVAudioResampleContext *avr);
+
+/**
+ * Close an AudioMix context.
+ *
+ * This clears and frees the mixing matrix arrays.
+ */
+void ff_audio_mix_close(AudioMix *am);
+
+/**
+ * Apply channel mixing to audio data using the current mixing matrix.
+ */
+int ff_audio_mix(AudioMix *am, AudioData *src);
+
+/* arch-specific initialization functions */
+
+void ff_audio_mix_init_x86(AudioMix *am);
+
+#endif /* AVRESAMPLE_AUDIO_MIX_H */
diff --git a/libavresample/audio_mix_matrix.c b/libavresample/audio_mix_matrix.c
new file mode 100644 (file)
index 0000000..96c49ef
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/libm.h"
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+#include "internal.h"
+#include "audio_data.h"
+#include "audio_mix.h"
+
+/* channel positions */
+#define FRONT_LEFT              0
+#define FRONT_RIGHT             1
+#define FRONT_CENTER            2
+#define LOW_FREQUENCY           3
+#define BACK_LEFT               4
+#define BACK_RIGHT              5
+#define FRONT_LEFT_OF_CENTER    6
+#define FRONT_RIGHT_OF_CENTER   7
+#define BACK_CENTER             8
+#define SIDE_LEFT               9
+#define SIDE_RIGHT             10
+#define TOP_CENTER             11
+#define TOP_FRONT_LEFT         12
+#define TOP_FRONT_CENTER       13
+#define TOP_FRONT_RIGHT        14
+#define TOP_BACK_LEFT          15
+#define TOP_BACK_CENTER        16
+#define TOP_BACK_RIGHT         17
+#define STEREO_LEFT            29
+#define STEREO_RIGHT           30
+#define WIDE_LEFT              31
+#define WIDE_RIGHT             32
+#define SURROUND_DIRECT_LEFT   33
+#define SURROUND_DIRECT_RIGHT  34
+
+static av_always_inline int even(uint64_t layout)
+{
+    return (!layout || (layout & (layout - 1)));
+}
+
+static int sane_layout(uint64_t layout)
+{
+    /* check that there is at least 1 front speaker */
+    if (!(layout & AV_CH_LAYOUT_SURROUND))
+        return 0;
+
+    /* check for left/right symmetry */
+    if (!even(layout & (AV_CH_FRONT_LEFT           | AV_CH_FRONT_RIGHT))           ||
+        !even(layout & (AV_CH_SIDE_LEFT            | AV_CH_SIDE_RIGHT))            ||
+        !even(layout & (AV_CH_BACK_LEFT            | AV_CH_BACK_RIGHT))            ||
+        !even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) ||
+        !even(layout & (AV_CH_TOP_FRONT_LEFT       | AV_CH_TOP_FRONT_RIGHT))       ||
+        !even(layout & (AV_CH_TOP_BACK_LEFT        | AV_CH_TOP_BACK_RIGHT))        ||
+        !even(layout & (AV_CH_STEREO_LEFT          | AV_CH_STEREO_RIGHT))          ||
+        !even(layout & (AV_CH_WIDE_LEFT            | AV_CH_WIDE_RIGHT))            ||
+        !even(layout & (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT)))
+        return 0;
+
+    return 1;
+}
+
+int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
+                            double center_mix_level, double surround_mix_level,
+                            double lfe_mix_level, int normalize,
+                            double *matrix_out, int stride)
+{
+    int i, j, out_i, out_j;
+    double matrix[64][64] = {{0}};
+    int64_t unaccounted = in_layout & ~out_layout;
+    double maxcoef = 0;
+    int in_channels, out_channels;
+
+    in_channels  = av_get_channel_layout_nb_channels( in_layout);
+    out_channels = av_get_channel_layout_nb_channels(out_layout);
+
+    memset(matrix_out, 0, out_channels * stride * sizeof(*matrix_out));
+
+    /* check if layouts are supported */
+    if (!in_layout || in_channels > AVRESAMPLE_MAX_CHANNELS)
+        return AVERROR(EINVAL);
+    if (!out_layout || out_channels > AVRESAMPLE_MAX_CHANNELS)
+        return AVERROR(EINVAL);
+
+    /* check if layouts are unbalanced or abnormal */
+    if (!sane_layout(in_layout) || !sane_layout(out_layout))
+        return AVERROR_PATCHWELCOME;
+
+    /* route matching input/output channels */
+    for (i = 0; i < 64; i++) {
+        if (in_layout & out_layout & (1ULL << i))
+            matrix[i][i] = 1.0;
+    }
+
+    /* mix front center to front left/right */
+    if (unaccounted & AV_CH_FRONT_CENTER) {
+        if ((out_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) {
+            matrix[FRONT_LEFT ][FRONT_CENTER] += M_SQRT1_2;
+            matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix front left/right to center */
+    if (unaccounted & AV_CH_LAYOUT_STEREO) {
+        if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][FRONT_LEFT ] += M_SQRT1_2;
+            matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
+            /* mix left/right/center to center */
+            if (in_layout & AV_CH_FRONT_CENTER)
+                matrix[FRONT_CENTER][FRONT_CENTER] = center_mix_level * M_SQRT2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix back center to back, side, or front */
+    if (unaccounted & AV_CH_BACK_CENTER) {
+        if (out_layout & AV_CH_BACK_LEFT) {
+            matrix[BACK_LEFT ][BACK_CENTER] += M_SQRT1_2;
+            matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
+        } else if (out_layout & AV_CH_SIDE_LEFT) {
+            matrix[SIDE_LEFT ][BACK_CENTER] += M_SQRT1_2;
+            matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
+        } else if (out_layout & AV_CH_FRONT_LEFT) {
+            matrix[FRONT_LEFT ][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
+            matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
+        } else if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][BACK_CENTER] += surround_mix_level * M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix back left/right to back center, side, or front */
+    if (unaccounted & AV_CH_BACK_LEFT) {
+        if (out_layout & AV_CH_BACK_CENTER) {
+            matrix[BACK_CENTER][BACK_LEFT ] += M_SQRT1_2;
+            matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
+        } else if (out_layout & AV_CH_SIDE_LEFT) {
+            /* if side channels do not exist in the input, just copy back
+               channels to side channels, otherwise mix back into side */
+            if (in_layout & AV_CH_SIDE_LEFT) {
+                matrix[SIDE_LEFT ][BACK_LEFT ] += M_SQRT1_2;
+                matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
+            } else {
+                matrix[SIDE_LEFT ][BACK_LEFT ] += 1.0;
+                matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
+            }
+        } else if (out_layout & AV_CH_FRONT_LEFT) {
+            matrix[FRONT_LEFT ][BACK_LEFT ] += surround_mix_level;
+            matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level;
+        } else if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][BACK_LEFT ] += surround_mix_level * M_SQRT1_2;
+            matrix[FRONT_CENTER][BACK_RIGHT] += surround_mix_level * M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix side left/right into back or front */
+    if (unaccounted & AV_CH_SIDE_LEFT) {
+        if (out_layout & AV_CH_BACK_LEFT) {
+            /* if back channels do not exist in the input, just copy side
+               channels to back channels, otherwise mix side into back */
+            if (in_layout & AV_CH_BACK_LEFT) {
+                matrix[BACK_LEFT ][SIDE_LEFT ] += M_SQRT1_2;
+                matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
+            } else {
+                matrix[BACK_LEFT ][SIDE_LEFT ] += 1.0;
+                matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
+            }
+        } else if (out_layout & AV_CH_BACK_CENTER) {
+            matrix[BACK_CENTER][SIDE_LEFT ] += M_SQRT1_2;
+            matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
+        } else if (out_layout & AV_CH_FRONT_LEFT) {
+            matrix[FRONT_LEFT ][SIDE_LEFT ] += surround_mix_level;
+            matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level;
+        } else if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2;
+            matrix[FRONT_CENTER][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix left-of-center/right-of-center into front left/right or center */
+    if (unaccounted & AV_CH_FRONT_LEFT_OF_CENTER) {
+        if (out_layout & AV_CH_FRONT_LEFT) {
+            matrix[FRONT_LEFT ][FRONT_LEFT_OF_CENTER ] += 1.0;
+            matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
+        } else if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER ] += M_SQRT1_2;
+            matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+    /* mix LFE into front left/right or center */
+    if (unaccounted & AV_CH_LOW_FREQUENCY) {
+        if (out_layout & AV_CH_FRONT_CENTER) {
+            matrix[FRONT_CENTER][LOW_FREQUENCY] += lfe_mix_level;
+        } else if (out_layout & AV_CH_FRONT_LEFT) {
+            matrix[FRONT_LEFT ][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2;
+            matrix[FRONT_RIGHT][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2;
+        } else
+            return AVERROR_PATCHWELCOME;
+    }
+
+    /* transfer internal matrix to output matrix and calculate maximum
+       per-channel coefficient sum */
+    for (out_i = i = 0; out_i < out_channels && i < 64; i++) {
+        double sum = 0;
+        for (out_j = j = 0; out_j < in_channels && j < 64; j++) {
+            matrix_out[out_i * stride + out_j] = matrix[i][j];
+            sum += fabs(matrix[i][j]);
+            if (in_layout & (1ULL << j))
+                out_j++;
+        }
+        maxcoef = FFMAX(maxcoef, sum);
+        if (out_layout & (1ULL << i))
+            out_i++;
+    }
+
+    /* normalize */
+    if (normalize && maxcoef > 1.0) {
+        for (i = 0; i < out_channels; i++)
+            for (j = 0; j < in_channels; j++)
+                matrix_out[i * stride + j] /= maxcoef;
+    }
+
+    return 0;
+}
+
+int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix,
+                          int stride)
+{
+    int in_channels, out_channels, i, o;
+
+    in_channels  = av_get_channel_layout_nb_channels(avr->in_channel_layout);
+    out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
+
+    if ( in_channels < 0 ||  in_channels > AVRESAMPLE_MAX_CHANNELS ||
+        out_channels < 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n");
+        return AVERROR(EINVAL);
+    }
+
+    switch (avr->mix_coeff_type) {
+    case AV_MIX_COEFF_TYPE_Q6:
+        if (!avr->am->matrix_q6[0]) {
+            av_log(avr, AV_LOG_ERROR, "matrix is not set\n");
+            return AVERROR(EINVAL);
+        }
+        for (o = 0; o < out_channels; o++)
+            for (i = 0; i < in_channels; i++)
+                matrix[o * stride + i] = avr->am->matrix_q6[o][i] / 64.0;
+        break;
+    case AV_MIX_COEFF_TYPE_Q15:
+        if (!avr->am->matrix_q15[0]) {
+            av_log(avr, AV_LOG_ERROR, "matrix is not set\n");
+            return AVERROR(EINVAL);
+        }
+        for (o = 0; o < out_channels; o++)
+            for (i = 0; i < in_channels; i++)
+                matrix[o * stride + i] = avr->am->matrix_q15[o][i] / 32768.0;
+        break;
+    case AV_MIX_COEFF_TYPE_FLT:
+        if (!avr->am->matrix_flt[0]) {
+            av_log(avr, AV_LOG_ERROR, "matrix is not set\n");
+            return AVERROR(EINVAL);
+        }
+        for (o = 0; o < out_channels; o++)
+            for (i = 0; i < in_channels; i++)
+                matrix[o * stride + i] = avr->am->matrix_flt[o][i];
+        break;
+    default:
+        av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n");
+        return AVERROR(EINVAL);
+    }
+    return 0;
+}
+
+int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
+                          int stride)
+{
+    int in_channels, out_channels, i, o;
+
+    in_channels  = av_get_channel_layout_nb_channels(avr->in_channel_layout);
+    out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
+
+    if ( in_channels < 0 ||  in_channels > AVRESAMPLE_MAX_CHANNELS ||
+        out_channels < 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (avr->am->matrix)
+        av_freep(avr->am->matrix);
+
+#define CONVERT_MATRIX(type, expr)                                          \
+    avr->am->matrix_## type[0] = av_mallocz(out_channels * in_channels *    \
+                                            sizeof(*avr->am->matrix_## type[0])); \
+    if (!avr->am->matrix_## type[0])                                        \
+        return AVERROR(ENOMEM);                                             \
+    for (o = 0; o < out_channels; o++) {                                    \
+        if (o > 0)                                                          \
+            avr->am->matrix_## type[o] = avr->am->matrix_## type[o - 1] +   \
+                                         in_channels;                       \
+        for (i = 0; i < in_channels; i++) {                                 \
+            double v = matrix[o * stride + i];                              \
+            avr->am->matrix_## type[o][i] = expr;                           \
+        }                                                                   \
+    }                                                                       \
+    avr->am->matrix = (void **)avr->am->matrix_## type;
+
+    switch (avr->mix_coeff_type) {
+    case AV_MIX_COEFF_TYPE_Q6:
+        CONVERT_MATRIX(q6, av_clip_int16(lrint(64.0 * v)))
+        break;
+    case AV_MIX_COEFF_TYPE_Q15:
+        CONVERT_MATRIX(q15, av_clipl_int32(llrint(32768.0 * v)))
+        break;
+    case AV_MIX_COEFF_TYPE_FLT:
+        CONVERT_MATRIX(flt, v)
+        break;
+    default:
+        av_log(avr, AV_LOG_ERROR, "Invalid mix coeff type\n");
+        return AVERROR(EINVAL);
+    }
+
+    /* TODO: detect situations where we can just swap around pointers
+             instead of doing matrix multiplications with 0.0 and 1.0 */
+
+    return 0;
+}
diff --git a/libavresample/avresample-test.c b/libavresample/avresample-test.c
new file mode 100644 (file)
index 0000000..ad2f16d
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2002 Fabrice Bellard
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/lfg.h"
+#include "libavutil/libm.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+
+static double dbl_rand(AVLFG *lfg)
+{
+    return 2.0 * (av_lfg_get(lfg) / (double)UINT_MAX) - 1.0;
+}
+
+#define PUT_FUNC(name, fmt, type, expr)                                     \
+static void put_sample_ ## name(void **data, enum AVSampleFormat sample_fmt,\
+                                int channels, int sample, int ch,           \
+                                double v_dbl)                               \
+{                                                                           \
+    type v = expr;                                                          \
+    type **out = (type **)data;                                             \
+    if (av_sample_fmt_is_planar(sample_fmt))                                \
+        out[ch][sample] = v;                                                \
+    else                                                                    \
+        out[0][sample * channels + ch] = v;                                 \
+}
+
+PUT_FUNC(u8,  AV_SAMPLE_FMT_U8,  uint8_t, av_clip_uint8 ( lrint(v_dbl * (1  <<  7)) + 128))
+PUT_FUNC(s16, AV_SAMPLE_FMT_S16, int16_t, av_clip_int16 ( lrint(v_dbl * (1  << 15))))
+PUT_FUNC(s32, AV_SAMPLE_FMT_S32, int32_t, av_clipl_int32(llrint(v_dbl * (1U << 31))))
+PUT_FUNC(flt, AV_SAMPLE_FMT_FLT, float,   v_dbl)
+PUT_FUNC(dbl, AV_SAMPLE_FMT_DBL, double,  v_dbl)
+
+static void put_sample(void **data, enum AVSampleFormat sample_fmt,
+                       int channels, int sample, int ch, double v_dbl)
+{
+    switch (av_get_packed_sample_fmt(sample_fmt)) {
+    case AV_SAMPLE_FMT_U8:
+        put_sample_u8(data, sample_fmt, channels, sample, ch, v_dbl);
+        break;
+    case AV_SAMPLE_FMT_S16:
+        put_sample_s16(data, sample_fmt, channels, sample, ch, v_dbl);
+        break;
+    case AV_SAMPLE_FMT_S32:
+        put_sample_s32(data, sample_fmt, channels, sample, ch, v_dbl);
+        break;
+    case AV_SAMPLE_FMT_FLT:
+        put_sample_flt(data, sample_fmt, channels, sample, ch, v_dbl);
+        break;
+    case AV_SAMPLE_FMT_DBL:
+        put_sample_dbl(data, sample_fmt, channels, sample, ch, v_dbl);
+        break;
+    }
+}
+
+static void audiogen(AVLFG *rnd, void **data, enum AVSampleFormat sample_fmt,
+                     int channels, int sample_rate, int nb_samples)
+{
+    int i, ch, k;
+    double v, f, a, ampa;
+    double tabf1[AVRESAMPLE_MAX_CHANNELS];
+    double tabf2[AVRESAMPLE_MAX_CHANNELS];
+    double taba[AVRESAMPLE_MAX_CHANNELS];
+
+#define PUT_SAMPLE put_sample(data, sample_fmt, channels, k, ch, v);
+
+    k = 0;
+
+    /* 1 second of single freq sinus at 1000 Hz */
+    a = 0;
+    for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) {
+        v = sin(a) * 0.30;
+        for (ch = 0; ch < channels; ch++)
+            PUT_SAMPLE
+        a += M_PI * 1000.0 * 2.0 / sample_rate;
+    }
+
+    /* 1 second of varing frequency between 100 and 10000 Hz */
+    a = 0;
+    for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) {
+        v = sin(a) * 0.30;
+        for (ch = 0; ch < channels; ch++)
+            PUT_SAMPLE
+        f  = 100.0 + (((10000.0 - 100.0) * i) / sample_rate);
+        a += M_PI * f * 2.0 / sample_rate;
+    }
+
+    /* 0.5 second of low amplitude white noise */
+    for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) {
+        v = dbl_rand(rnd) * 0.30;
+        for (ch = 0; ch < channels; ch++)
+            PUT_SAMPLE
+    }
+
+    /* 0.5 second of high amplitude white noise */
+    for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) {
+        v = dbl_rand(rnd);
+        for (ch = 0; ch < channels; ch++)
+            PUT_SAMPLE
+    }
+
+    /* 1 second of unrelated ramps for each channel */
+    for (ch = 0; ch < channels; ch++) {
+        taba[ch]  = 0;
+        tabf1[ch] = 100 + av_lfg_get(rnd) % 5000;
+        tabf2[ch] = 100 + av_lfg_get(rnd) % 5000;
+    }
+    for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) {
+        for (ch = 0; ch < channels; ch++) {
+            v = sin(taba[ch]) * 0.30;
+            PUT_SAMPLE
+            f = tabf1[ch] + (((tabf2[ch] - tabf1[ch]) * i) / sample_rate);
+            taba[ch] += M_PI * f * 2.0 / sample_rate;
+        }
+    }
+
+    /* 2 seconds of 500 Hz with varying volume */
+    a    = 0;
+    ampa = 0;
+    for (i = 0; i < 2 * sample_rate && k < nb_samples; i++, k++) {
+        for (ch = 0; ch < channels; ch++) {
+            double amp = (1.0 + sin(ampa)) * 0.15;
+            if (ch & 1)
+                amp = 0.30 - amp;
+            v = sin(a) * amp;
+            PUT_SAMPLE
+            a    += M_PI * 500.0 * 2.0 / sample_rate;
+            ampa += M_PI *  2.0 / sample_rate;
+        }
+    }
+}
+
+/* formats, rates, and layouts are ordered for priority in testing.
+   e.g. 'avresample-test 4 2 2' will test all input/output combinations of
+   S16/FLTP/S16P/FLT, 48000/44100, and stereo/mono */
+
+static const enum AVSampleFormat formats[] = {
+    AV_SAMPLE_FMT_S16,
+    AV_SAMPLE_FMT_FLTP,
+    AV_SAMPLE_FMT_S16P,
+    AV_SAMPLE_FMT_FLT,
+    AV_SAMPLE_FMT_S32P,
+    AV_SAMPLE_FMT_S32,
+    AV_SAMPLE_FMT_U8P,
+    AV_SAMPLE_FMT_U8,
+    AV_SAMPLE_FMT_DBLP,
+    AV_SAMPLE_FMT_DBL,
+};
+
+static const int rates[] = {
+    48000,
+    44100,
+    16000
+};
+
+static const uint64_t layouts[] = {
+    AV_CH_LAYOUT_STEREO,
+    AV_CH_LAYOUT_MONO,
+    AV_CH_LAYOUT_5POINT1,
+    AV_CH_LAYOUT_7POINT1,
+};
+
+int main(int argc, char **argv)
+{
+    AVAudioResampleContext *s;
+    AVLFG rnd;
+    int ret = 0;
+    uint8_t *in_buf = NULL;
+    uint8_t *out_buf = NULL;
+    unsigned int in_buf_size;
+    unsigned int out_buf_size;
+    uint8_t  *in_data[AVRESAMPLE_MAX_CHANNELS] = { 0 };
+    uint8_t *out_data[AVRESAMPLE_MAX_CHANNELS] = { 0 };
+    int in_linesize;
+    int out_linesize;
+    uint64_t in_ch_layout;
+    int in_channels;
+    enum AVSampleFormat in_fmt;
+    int in_rate;
+    uint64_t out_ch_layout;
+    int out_channels;
+    enum AVSampleFormat out_fmt;
+    int out_rate;
+    int num_formats, num_rates, num_layouts;
+    int i, j, k, l, m, n;
+
+    num_formats = 2;
+    num_rates   = 2;
+    num_layouts = 2;
+    if (argc > 1) {
+        if (!av_strncasecmp(argv[1], "-h", 3)) {
+            av_log(NULL, AV_LOG_INFO, "Usage: avresample-test [<num formats> "
+                   "[<num sample rates> [<num channel layouts>]]]\n"
+                   "Default is 2 2 2\n");
+            return 0;
+        }
+        num_formats = strtol(argv[1], NULL, 0);
+        num_formats = av_clip(num_formats, 1, FF_ARRAY_ELEMS(formats));
+    }
+    if (argc > 2) {
+        num_rates = strtol(argv[2], NULL, 0);
+        num_rates = av_clip(num_rates, 1, FF_ARRAY_ELEMS(rates));
+    }
+    if (argc > 3) {
+        num_layouts = strtol(argv[3], NULL, 0);
+        num_layouts = av_clip(num_layouts, 1, FF_ARRAY_ELEMS(layouts));
+    }
+
+    av_log_set_level(AV_LOG_DEBUG);
+
+    av_lfg_init(&rnd, 0xC0FFEE);
+
+    in_buf_size = av_samples_get_buffer_size(&in_linesize, 8, 48000 * 6,
+                                             AV_SAMPLE_FMT_DBLP, 0);
+    out_buf_size = in_buf_size;
+
+    in_buf = av_malloc(in_buf_size);
+    if (!in_buf)
+        goto end;
+    out_buf = av_malloc(out_buf_size);
+    if (!out_buf)
+        goto end;
+
+    s = avresample_alloc_context();
+    if (!s) {
+        av_log(NULL, AV_LOG_ERROR, "Error allocating AVAudioResampleContext\n");
+        ret = 1;
+        goto end;
+    }
+
+    for (i = 0; i < num_formats; i++) {
+        in_fmt = formats[i];
+        for (k = 0; k < num_layouts; k++) {
+            in_ch_layout = layouts[k];
+            in_channels  = av_get_channel_layout_nb_channels(in_ch_layout);
+            for (m = 0; m < num_rates; m++) {
+                in_rate = rates[m];
+
+                ret = av_samples_fill_arrays(in_data, &in_linesize, in_buf,
+                                             in_channels, in_rate * 6,
+                                             in_fmt, 0);
+                if (ret < 0) {
+                    av_log(s, AV_LOG_ERROR, "failed in_data fill arrays\n");
+                    goto end;
+                }
+                audiogen(&rnd, (void **)in_data, in_fmt, in_channels, in_rate, in_rate * 6);
+
+                for (j = 0; j < num_formats; j++) {
+                    out_fmt = formats[j];
+                    for (l = 0; l < num_layouts; l++) {
+                        out_ch_layout = layouts[l];
+                        out_channels  = av_get_channel_layout_nb_channels(out_ch_layout);
+                        for (n = 0; n < num_rates; n++) {
+                            out_rate = rates[n];
+
+                            av_log(NULL, AV_LOG_INFO, "%s to %s, %d to %d channels, %d Hz to %d Hz\n",
+                                   av_get_sample_fmt_name(in_fmt), av_get_sample_fmt_name(out_fmt),
+                                   in_channels, out_channels, in_rate, out_rate);
+
+                            ret = av_samples_fill_arrays(out_data, &out_linesize,
+                                                         out_buf, out_channels,
+                                                         out_rate * 6, out_fmt, 0);
+                            if (ret < 0) {
+                                av_log(s, AV_LOG_ERROR, "failed out_data fill arrays\n");
+                                goto end;
+                            }
+
+                            av_opt_set_int(s, "in_channel_layout",  in_ch_layout,  0);
+                            av_opt_set_int(s, "in_sample_fmt",      in_fmt,        0);
+                            av_opt_set_int(s, "in_sample_rate",     in_rate,       0);
+                            av_opt_set_int(s, "out_channel_layout", out_ch_layout, 0);
+                            av_opt_set_int(s, "out_sample_fmt",     out_fmt,       0);
+                            av_opt_set_int(s, "out_sample_rate",    out_rate,      0);
+
+                            av_opt_set_int(s, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
+
+                            ret = avresample_open(s);
+                            if (ret < 0) {
+                                av_log(s, AV_LOG_ERROR, "Error opening context\n");
+                                goto end;
+                            }
+
+                            ret = avresample_convert(s, (void **)out_data, out_linesize, out_rate * 6,
+                                                        (void **) in_data,  in_linesize,  in_rate * 6);
+                            if (ret < 0) {
+                                char errbuf[256];
+                                av_strerror(ret, errbuf, sizeof(errbuf));
+                                av_log(NULL, AV_LOG_ERROR, "%s\n", errbuf);
+                                goto end;
+                            }
+                            av_log(NULL, AV_LOG_INFO, "Converted %d samples to %d samples\n",
+                                   in_rate * 6, ret);
+                            if (avresample_get_delay(s) > 0)
+                                av_log(NULL, AV_LOG_INFO, "%d delay samples not converted\n",
+                                       avresample_get_delay(s));
+                            if (avresample_available(s) > 0)
+                                av_log(NULL, AV_LOG_INFO, "%d samples available for output\n",
+                                       avresample_available(s));
+                            av_log(NULL, AV_LOG_INFO, "\n");
+
+                            avresample_close(s);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    ret = 0;
+
+end:
+    av_freep(&in_buf);
+    av_freep(&out_buf);
+    avresample_free(&s);
+    return ret;
+}
diff --git a/libavresample/avresample.h b/libavresample/avresample.h
new file mode 100644 (file)
index 0000000..41688ed
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_AVRESAMPLE_H
+#define AVRESAMPLE_AVRESAMPLE_H
+
+/**
+ * @file
+ * external API header
+ */
+
+#include "libavutil/audioconvert.h"
+#include "libavutil/avutil.h"
+#include "libavutil/dict.h"
+#include "libavutil/log.h"
+
+#include "libavresample/version.h"
+
+#define AVRESAMPLE_MAX_CHANNELS 32
+
+typedef struct AVAudioResampleContext AVAudioResampleContext;
+
+/** Mixing Coefficient Types */
+enum AVMixCoeffType {
+    AV_MIX_COEFF_TYPE_Q6,   /** 16-bit 10.6 fixed-point                     */
+    AV_MIX_COEFF_TYPE_Q15,  /** 32-bit 17.15 fixed-point                    */
+    AV_MIX_COEFF_TYPE_FLT,  /** floating-point                              */
+    AV_MIX_COEFF_TYPE_NB,   /** Number of coeff types. Not part of ABI      */
+};
+
+/**
+ * Return the LIBAVRESAMPLE_VERSION_INT constant.
+ */
+unsigned avresample_version(void);
+
+/**
+ * Return the libavresample build-time configuration.
+ * @return  configure string
+ */
+const char *avresample_configuration(void);
+
+/**
+ * Return the libavresample license.
+ */
+const char *avresample_license(void);
+
+/**
+ * Get the AVClass for AVAudioResampleContext.
+ *
+ * Can be used in combination with AV_OPT_SEARCH_FAKE_OBJ for examining options
+ * without allocating a context.
+ *
+ * @see av_opt_find().
+ *
+ * @return AVClass for AVAudioResampleContext
+ */
+const AVClass *avresample_get_class(void);
+
+/**
+ * Allocate AVAudioResampleContext and set options.
+ *
+ * @return  allocated audio resample context, or NULL on failure
+ */
+AVAudioResampleContext *avresample_alloc_context(void);
+
+/**
+ * Initialize AVAudioResampleContext.
+ *
+ * @param avr  audio resample context
+ * @return     0 on success, negative AVERROR code on failure
+ */
+int avresample_open(AVAudioResampleContext *avr);
+
+/**
+ * Close AVAudioResampleContext.
+ *
+ * This closes the context, but it does not change the parameters. The context
+ * can be reopened with avresample_open(). It does, however, clear the output
+ * FIFO and any remaining leftover samples in the resampling delay buffer. If
+ * there was a custom matrix being used, that is also cleared.
+ *
+ * @see avresample_convert()
+ * @see avresample_set_matrix()
+ *
+ * @param avr  audio resample context
+ */
+void avresample_close(AVAudioResampleContext *avr);
+
+/**
+ * Free AVAudioResampleContext and associated AVOption values.
+ *
+ * This also calls avresample_close() before freeing.
+ *
+ * @param avr  audio resample context
+ */
+void avresample_free(AVAudioResampleContext **avr);
+
+/**
+ * Generate a channel mixing matrix.
+ *
+ * This function is the one used internally by libavresample for building the
+ * default mixing matrix. It is made public just as a utility function for
+ * building custom matrices.
+ *
+ * @param in_layout           input channel layout
+ * @param out_layout          output channel layout
+ * @param center_mix_level    mix level for the center channel
+ * @param surround_mix_level  mix level for the surround channel(s)
+ * @param lfe_mix_level       mix level for the low-frequency effects channel
+ * @param normalize           if 1, coefficients will be normalized to prevent
+ *                            overflow. if 0, coefficients will not be
+ *                            normalized.
+ * @param[out] matrix         mixing coefficients; matrix[i + stride * o] is
+ *                            the weight of input channel i in output channel o.
+ * @param stride              distance between adjacent input channels in the
+ *                            matrix array
+ * @return                    0 on success, negative AVERROR code on failure
+ */
+int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout,
+                            double center_mix_level, double surround_mix_level,
+                            double lfe_mix_level, int normalize, double *matrix,
+                            int stride);
+
+/**
+ * Get the current channel mixing matrix.
+ *
+ * @param avr     audio resample context
+ * @param matrix  mixing coefficients; matrix[i + stride * o] is the weight of
+ *                input channel i in output channel o.
+ * @param stride  distance between adjacent input channels in the matrix array
+ * @return        0 on success, negative AVERROR code on failure
+ */
+int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix,
+                          int stride);
+
+/**
+ * Set channel mixing matrix.
+ *
+ * Allows for setting a custom mixing matrix, overriding the default matrix
+ * generated internally during avresample_open(). This function can be called
+ * anytime on an allocated context, either before or after calling
+ * avresample_open(). avresample_convert() always uses the current matrix.
+ * Calling avresample_close() on the context will clear the current matrix.
+ *
+ * @see avresample_close()
+ *
+ * @param avr     audio resample context
+ * @param matrix  mixing coefficients; matrix[i + stride * o] is the weight of
+ *                input channel i in output channel o.
+ * @param stride  distance between adjacent input channels in the matrix array
+ * @return        0 on success, negative AVERROR code on failure
+ */
+int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix,
+                          int stride);
+
+/**
+ * Set compensation for resampling.
+ *
+ * This can be called anytime after avresample_open(). If resampling was not
+ * being done previously, the AVAudioResampleContext is closed and reopened
+ * with resampling enabled. In this case, any samples remaining in the output
+ * FIFO and the current channel mixing matrix will be restored after reopening
+ * the context.
+ *
+ * @param avr                    audio resample context
+ * @param sample_delta           compensation delta, in samples
+ * @param compensation_distance  compensation distance, in samples
+ * @return                       0 on success, negative AVERROR code on failure
+ */
+int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta,
+                                int compensation_distance);
+
+/**
+ * Convert input samples and write them to the output FIFO.
+ *
+ * The output data can be NULL or have fewer allocated samples than required.
+ * In this case, any remaining samples not written to the output will be added
+ * to an internal FIFO buffer, to be returned at the next call to this function
+ * or to avresample_read().
+ *
+ * If converting sample rate, there may be data remaining in the internal
+ * resampling delay buffer. avresample_get_delay() tells the number of remaining
+ * samples. To get this data as output, call avresample_convert() with NULL
+ * input.
+ *
+ * At the end of the conversion process, there may be data remaining in the
+ * internal FIFO buffer. avresample_available() tells the number of remaining
+ * samples. To get this data as output, either call avresample_convert() with
+ * NULL input or call avresample_read().
+ *
+ * @see avresample_available()
+ * @see avresample_read()
+ * @see avresample_get_delay()
+ *
+ * @param avr             audio resample context
+ * @param output          output data pointers
+ * @param out_plane_size  output plane size, in bytes.
+ *                        This can be 0 if unknown, but that will lead to
+ *                        optimized functions not being used directly on the
+ *                        output, which could slow down some conversions.
+ * @param out_samples     maximum number of samples that the output buffer can hold
+ * @param input           input data pointers
+ * @param in_plane_size   input plane size, in bytes
+ *                        This can be 0 if unknown, but that will lead to
+ *                        optimized functions not being used directly on the
+ *                        input, which could slow down some conversions.
+ * @param in_samples      number of input samples to convert
+ * @return                number of samples written to the output buffer,
+ *                        not including converted samples added to the internal
+ *                        output FIFO
+ */
+int avresample_convert(AVAudioResampleContext *avr, void **output,
+                       int out_plane_size, int out_samples, void **input,
+                       int in_plane_size, int in_samples);
+
+/**
+ * Return the number of samples currently in the resampling delay buffer.
+ *
+ * When resampling, there may be a delay between the input and output. Any
+ * unconverted samples in each call are stored internally in a delay buffer.
+ * This function allows the user to determine the current number of samples in
+ * the delay buffer, which can be useful for synchronization.
+ *
+ * @see avresample_convert()
+ *
+ * @param avr  audio resample context
+ * @return     number of samples currently in the resampling delay buffer
+ */
+int avresample_get_delay(AVAudioResampleContext *avr);
+
+/**
+ * Return the number of available samples in the output FIFO.
+ *
+ * During conversion, if the user does not specify an output buffer or
+ * specifies an output buffer that is smaller than what is needed, remaining
+ * samples that are not written to the output are stored to an internal FIFO
+ * buffer. The samples in the FIFO can be read with avresample_read() or
+ * avresample_convert().
+ *
+ * @see avresample_read()
+ * @see avresample_convert()
+ *
+ * @param avr  audio resample context
+ * @return     number of samples available for reading
+ */
+int avresample_available(AVAudioResampleContext *avr);
+
+/**
+ * Read samples from the output FIFO.
+ *
+ * During conversion, if the user does not specify an output buffer or
+ * specifies an output buffer that is smaller than what is needed, remaining
+ * samples that are not written to the output are stored to an internal FIFO
+ * buffer. This function can be used to read samples from that internal FIFO.
+ *
+ * @see avresample_available()
+ * @see avresample_convert()
+ *
+ * @param avr         audio resample context
+ * @param output      output data pointers
+ * @param nb_samples  number of samples to read from the FIFO
+ * @return            the number of samples written to output
+ */
+int avresample_read(AVAudioResampleContext *avr, void **output, int nb_samples);
+
+#endif /* AVRESAMPLE_AVRESAMPLE_H */
diff --git a/libavresample/internal.h b/libavresample/internal.h
new file mode 100644 (file)
index 0000000..49ea6a6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_INTERNAL_H
+#define AVRESAMPLE_INTERNAL_H
+
+#include "libavutil/audio_fifo.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/samplefmt.h"
+#include "avresample.h"
+#include "audio_convert.h"
+#include "audio_data.h"
+#include "audio_mix.h"
+#include "resample.h"
+
+struct AVAudioResampleContext {
+    const AVClass *av_class;        /**< AVClass for logging and AVOptions  */
+
+    uint64_t in_channel_layout;                 /**< input channel layout   */
+    enum AVSampleFormat in_sample_fmt;          /**< input sample format    */
+    int in_sample_rate;                         /**< input sample rate      */
+    uint64_t out_channel_layout;                /**< output channel layout  */
+    enum AVSampleFormat out_sample_fmt;         /**< output sample format   */
+    int out_sample_rate;                        /**< output sample rate     */
+    enum AVSampleFormat internal_sample_fmt;    /**< internal sample format */
+    enum AVMixCoeffType mix_coeff_type;         /**< mixing coefficient type */
+    double center_mix_level;                    /**< center mix level       */
+    double surround_mix_level;                  /**< surround mix level     */
+    double lfe_mix_level;                       /**< lfe mix level          */
+    int force_resampling;                       /**< force resampling       */
+    int filter_size;                            /**< length of each FIR filter in the resampling filterbank relative to the cutoff frequency */
+    int phase_shift;                            /**< log2 of the number of entries in the resampling polyphase filterbank */
+    int linear_interp;                          /**< if 1 then the resampling FIR filter will be linearly interpolated */
+    double cutoff;                              /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */
+
+    int in_channels;        /**< number of input channels                   */
+    int out_channels;       /**< number of output channels                  */
+    int resample_channels;  /**< number of channels used for resampling     */
+    int downmix_needed;     /**< downmixing is needed                       */
+    int upmix_needed;       /**< upmixing is needed                         */
+    int mixing_needed;      /**< either upmixing or downmixing is needed    */
+    int resample_needed;    /**< resampling is needed                       */
+    int in_convert_needed;  /**< input sample format conversion is needed   */
+    int out_convert_needed; /**< output sample format conversion is needed  */
+
+    AudioData *in_buffer;           /**< buffer for converted input         */
+    AudioData *resample_out_buffer; /**< buffer for output from resampler   */
+    AudioData *out_buffer;          /**< buffer for converted output        */
+    AVAudioFifo *out_fifo;          /**< FIFO for output samples            */
+
+    AudioConvert *ac_in;        /**< input sample format conversion context  */
+    AudioConvert *ac_out;       /**< output sample format conversion context */
+    ResampleContext *resample;  /**< resampling context                      */
+    AudioMix *am;               /**< channel mixing context                  */
+};
+
+#endif /* AVRESAMPLE_INTERNAL_H */
diff --git a/libavresample/libavresample.v b/libavresample/libavresample.v
new file mode 100644 (file)
index 0000000..b8c7c7d
--- /dev/null
@@ -0,0 +1,4 @@
+LIBAVRESAMPLE_$MAJOR {
+        global: av*;
+        local:  *;
+};
diff --git a/libavresample/options.c b/libavresample/options.c
new file mode 100644 (file)
index 0000000..0be1a26
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/mathematics.h"
+#include "libavutil/opt.h"
+#include "avresample.h"
+#include "internal.h"
+#include "audio_mix.h"
+
+/**
+ * @file
+ * Options definition for AVAudioResampleContext.
+ */
+
+#define OFFSET(x) offsetof(AVAudioResampleContext, x)
+#define PARAM AV_OPT_FLAG_AUDIO_PARAM
+
+static const AVOption options[] = {
+    { "in_channel_layout",      "Input Channel Layout",     OFFSET(in_channel_layout),      AV_OPT_TYPE_INT64,  { 0                     }, INT64_MIN,            INT64_MAX,              PARAM },
+    { "in_sample_fmt",          "Input Sample Format",      OFFSET(in_sample_fmt),          AV_OPT_TYPE_INT,    { AV_SAMPLE_FMT_S16     }, AV_SAMPLE_FMT_U8,     AV_SAMPLE_FMT_NB-1,     PARAM },
+    { "in_sample_rate",         "Input Sample Rate",        OFFSET(in_sample_rate),         AV_OPT_TYPE_INT,    { 48000                 }, 1,                    INT_MAX,                PARAM },
+    { "out_channel_layout",     "Output Channel Layout",    OFFSET(out_channel_layout),     AV_OPT_TYPE_INT64,  { 0                     }, INT64_MIN,            INT64_MAX,              PARAM },
+    { "out_sample_fmt",         "Output Sample Format",     OFFSET(out_sample_fmt),         AV_OPT_TYPE_INT,    { AV_SAMPLE_FMT_S16     }, AV_SAMPLE_FMT_U8,     AV_SAMPLE_FMT_NB-1,     PARAM },
+    { "out_sample_rate",        "Output Sample Rate",       OFFSET(out_sample_rate),        AV_OPT_TYPE_INT,    { 48000                 }, 1,                    INT_MAX,                PARAM },
+    { "internal_sample_fmt",    "Internal Sample Format",   OFFSET(internal_sample_fmt),    AV_OPT_TYPE_INT,    { AV_SAMPLE_FMT_FLTP    }, AV_SAMPLE_FMT_NONE,   AV_SAMPLE_FMT_NB-1,     PARAM },
+    { "mix_coeff_type",         "Mixing Coefficient Type",  OFFSET(mix_coeff_type),         AV_OPT_TYPE_INT,    { AV_MIX_COEFF_TYPE_FLT }, AV_MIX_COEFF_TYPE_Q6, AV_MIX_COEFF_TYPE_NB-1, PARAM, "mix_coeff_type" },
+        { "q6",  "16-bit 10.6 Fixed-Point",  0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_Q6  }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" },
+        { "q15", "32-bit 17.15 Fixed-Point", 0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_Q15 }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" },
+        { "flt", "Floating-Point",           0, AV_OPT_TYPE_CONST, { AV_MIX_COEFF_TYPE_FLT }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" },
+    { "center_mix_level",       "Center Mix Level",         OFFSET(center_mix_level),       AV_OPT_TYPE_DOUBLE, { M_SQRT1_2             }, -32.0,                32.0,                   PARAM },
+    { "surround_mix_level",     "Surround Mix Level",       OFFSET(surround_mix_level),     AV_OPT_TYPE_DOUBLE, { M_SQRT1_2             }, -32.0,                32.0,                   PARAM },
+    { "lfe_mix_level",          "LFE Mix Level",            OFFSET(lfe_mix_level),          AV_OPT_TYPE_DOUBLE, { 0.0                   }, -32.0,                32.0,                   PARAM },
+    { "force_resampling",       "Force Resampling",         OFFSET(force_resampling),       AV_OPT_TYPE_INT,    { 0                     }, 0,                    1,                      PARAM },
+    { "filter_size",            "Resampling Filter Size",   OFFSET(filter_size),            AV_OPT_TYPE_INT,    { 16                    }, 0,                    32, /* ??? */           PARAM },
+    { "phase_shift",            "Resampling Phase Shift",   OFFSET(phase_shift),            AV_OPT_TYPE_INT,    { 10                    }, 0,                    30, /* ??? */           PARAM },
+    { "linear_interp",          "Use Linear Interpolation", OFFSET(linear_interp),          AV_OPT_TYPE_INT,    { 0                     }, 0,                    1,                      PARAM },
+    { "cutoff",                 "Cutoff Frequency Ratio",   OFFSET(cutoff),                 AV_OPT_TYPE_DOUBLE, { 0.8                   }, 0.0,                  1.0,                    PARAM },
+    { NULL },
+};
+
+static const AVClass av_resample_context_class = {
+    .class_name = "AVAudioResampleContext",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVAudioResampleContext *avresample_alloc_context(void)
+{
+    AVAudioResampleContext *avr;
+
+    avr = av_mallocz(sizeof(*avr));
+    if (!avr)
+        return NULL;
+
+    avr->av_class = &av_resample_context_class;
+    av_opt_set_defaults(avr);
+
+    avr->am = av_mallocz(sizeof(*avr->am));
+    if (!avr->am) {
+        av_free(avr);
+        return NULL;
+    }
+    avr->am->avr = avr;
+
+    return avr;
+}
+
+const AVClass *avresample_get_class(void)
+{
+    return &av_resample_context_class;
+}
diff --git a/libavresample/resample.c b/libavresample/resample.c
new file mode 100644 (file)
index 0000000..5529faf
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/libm.h"
+#include "libavutil/log.h"
+#include "internal.h"
+#include "audio_data.h"
+
+#ifdef CONFIG_RESAMPLE_FLT
+/* float template */
+#define FILTER_SHIFT  0
+#define FELEM         float
+#define FELEM2        float
+#define FELEML        float
+#define WINDOW_TYPE   24
+#elifdef CONFIG_RESAMPLE_S32
+/* s32 template */
+#define FILTER_SHIFT  30
+#define FELEM         int32_t
+#define FELEM2        int64_t
+#define FELEML        int64_t
+#define FELEM_MAX     INT32_MAX
+#define FELEM_MIN     INT32_MIN
+#define WINDOW_TYPE   12
+#else
+/* s16 template */
+#define FILTER_SHIFT  15
+#define FELEM         int16_t
+#define FELEM2        int32_t
+#define FELEML        int64_t
+#define FELEM_MAX     INT16_MAX
+#define FELEM_MIN     INT16_MIN
+#define WINDOW_TYPE   9
+#endif
+
+struct ResampleContext {
+    AVAudioResampleContext *avr;
+    AudioData *buffer;
+    FELEM *filter_bank;
+    int filter_length;
+    int ideal_dst_incr;
+    int dst_incr;
+    int index;
+    int frac;
+    int src_incr;
+    int compensation_distance;
+    int phase_shift;
+    int phase_mask;
+    int linear;
+    double factor;
+};
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x)
+{
+    double v     = 1;
+    double lastv = 0;
+    double t     = 1;
+    int i;
+
+    x = x * x / 4;
+    for (i = 1; v != lastv; i++) {
+        lastv = v;
+        t    *= x / (i * i);
+        v    += t;
+    }
+    return v;
+}
+
+/**
+ * Build a polyphase filterbank.
+ *
+ * @param[out] filter       filter coefficients
+ * @param      factor       resampling factor
+ * @param      tap_count    tap count
+ * @param      phase_count  phase count
+ * @param      scale        wanted sum of coefficients for each filter
+ * @param      type         0->cubic
+ *                          1->blackman nuttall windowed sinc
+ *                          2..16->kaiser windowed sinc beta=2..16
+ * @return                  0 on success, negative AVERROR code on failure
+ */
+static int build_filter(FELEM *filter, double factor, int tap_count,
+                        int phase_count, int scale, int type)
+{
+    int ph, i;
+    double x, y, w;
+    double *tab;
+    const int center = (tap_count - 1) / 2;
+
+    tab = av_malloc(tap_count * sizeof(*tab));
+    if (!tab)
+        return AVERROR(ENOMEM);
+
+    /* if upsampling, only need to interpolate, no filter */
+    if (factor > 1.0)
+        factor = 1.0;
+
+    for (ph = 0; ph < phase_count; ph++) {
+        double norm = 0;
+        for (i = 0; i < tap_count; i++) {
+            x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+            if (x == 0) y = 1.0;
+            else        y = sin(x) / x;
+            switch (type) {
+            case 0: {
+                const float d = -0.5; //first order derivative = -0.5
+                x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+                if (x < 1.0) y = 1 - 3 * x*x + 2 * x*x*x + d * (                -x*x + x*x*x);
+                else         y =                           d * (-4 + 8 * x - 5 * x*x + x*x*x);
+                break;
+            }
+            case 1:
+                w  = 2.0 * x / (factor * tap_count) + M_PI;
+                y *= 0.3635819 - 0.4891775 * cos(    w) +
+                                 0.1365995 * cos(2 * w) -
+                                 0.0106411 * cos(3 * w);
+                break;
+            default:
+                w  = 2.0 * x / (factor * tap_count * M_PI);
+                y *= bessel(type * sqrt(FFMAX(1 - w * w, 0)));
+                break;
+            }
+
+            tab[i] = y;
+            norm  += y;
+        }
+
+        /* normalize so that an uniform color remains the same */
+        for (i = 0; i < tap_count; i++) {
+#ifdef CONFIG_RESAMPLE_FLT
+            filter[ph * tap_count + i] = tab[i] / norm;
+#else
+            filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm),
+                                                 FELEM_MIN, FELEM_MAX);
+#endif
+        }
+    }
+
+    av_free(tab);
+    return 0;
+}
+
+ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr)
+{
+    ResampleContext *c;
+    int out_rate    = avr->out_sample_rate;
+    int in_rate     = avr->in_sample_rate;
+    double factor   = FFMIN(out_rate * avr->cutoff / in_rate, 1.0);
+    int phase_count = 1 << avr->phase_shift;
+
+    /* TODO: add support for s32 and float internal formats */
+    if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P) {
+        av_log(avr, AV_LOG_ERROR, "Unsupported internal format for "
+               "resampling: %s\n",
+               av_get_sample_fmt_name(avr->internal_sample_fmt));
+        return NULL;
+    }
+    c = av_mallocz(sizeof(*c));
+    if (!c)
+        return NULL;
+
+    c->avr           = avr;
+    c->phase_shift   = avr->phase_shift;
+    c->phase_mask    = phase_count - 1;
+    c->linear        = avr->linear_interp;
+    c->factor        = factor;
+    c->filter_length = FFMAX((int)ceil(avr->filter_size / factor), 1);
+
+    c->filter_bank = av_mallocz(c->filter_length * (phase_count + 1) * sizeof(FELEM));
+    if (!c->filter_bank)
+        goto error;
+
+    if (build_filter(c->filter_bank, factor, c->filter_length, phase_count,
+                     1 << FILTER_SHIFT, WINDOW_TYPE) < 0)
+        goto error;
+
+    memcpy(&c->filter_bank[c->filter_length * phase_count + 1],
+           c->filter_bank, (c->filter_length - 1) * sizeof(FELEM));
+    c->filter_bank[c->filter_length * phase_count] = c->filter_bank[c->filter_length - 1];
+
+    c->compensation_distance = 0;
+    if (!av_reduce(&c->src_incr, &c->dst_incr, out_rate,
+                   in_rate * (int64_t)phase_count, INT32_MAX / 2))
+        goto error;
+    c->ideal_dst_incr = c->dst_incr;
+
+    c->index = -phase_count * ((c->filter_length - 1) / 2);
+    c->frac  = 0;
+
+    /* allocate internal buffer */
+    c->buffer = ff_audio_data_alloc(avr->resample_channels, 0,
+                                    avr->internal_sample_fmt,
+                                    "resample buffer");
+    if (!c->buffer)
+        goto error;
+
+    av_log(avr, AV_LOG_DEBUG, "resample: %s from %d Hz to %d Hz\n",
+           av_get_sample_fmt_name(avr->internal_sample_fmt),
+           avr->in_sample_rate, avr->out_sample_rate);
+
+    return c;
+
+error:
+    ff_audio_data_free(&c->buffer);
+    av_free(c->filter_bank);
+    av_free(c);
+    return NULL;
+}
+
+void ff_audio_resample_free(ResampleContext **c)
+{
+    if (!*c)
+        return;
+    ff_audio_data_free(&(*c)->buffer);
+    av_free((*c)->filter_bank);
+    av_freep(c);
+}
+
+int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta,
+                                int compensation_distance)
+{
+    ResampleContext *c;
+    AudioData *fifo_buf = NULL;
+    int ret = 0;
+
+    if (compensation_distance < 0)
+        return AVERROR(EINVAL);
+    if (!compensation_distance && sample_delta)
+        return AVERROR(EINVAL);
+
+    /* if resampling was not enabled previously, re-initialize the
+       AVAudioResampleContext and force resampling */
+    if (!avr->resample_needed) {
+        int fifo_samples;
+        double matrix[AVRESAMPLE_MAX_CHANNELS * AVRESAMPLE_MAX_CHANNELS] = { 0 };
+
+        /* buffer any remaining samples in the output FIFO before closing */
+        fifo_samples = av_audio_fifo_size(avr->out_fifo);
+        if (fifo_samples > 0) {
+            fifo_buf = ff_audio_data_alloc(avr->out_channels, fifo_samples,
+                                           avr->out_sample_fmt, NULL);
+            if (!fifo_buf)
+                return AVERROR(EINVAL);
+            ret = ff_audio_data_read_from_fifo(avr->out_fifo, fifo_buf,
+                                               fifo_samples);
+            if (ret < 0)
+                goto reinit_fail;
+        }
+        /* save the channel mixing matrix */
+        ret = avresample_get_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS);
+        if (ret < 0)
+            goto reinit_fail;
+
+        /* close the AVAudioResampleContext */
+        avresample_close(avr);
+
+        avr->force_resampling = 1;
+
+        /* restore the channel mixing matrix */
+        ret = avresample_set_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS);
+        if (ret < 0)
+            goto reinit_fail;
+
+        /* re-open the AVAudioResampleContext */
+        ret = avresample_open(avr);
+        if (ret < 0)
+            goto reinit_fail;
+
+        /* restore buffered samples to the output FIFO */
+        if (fifo_samples > 0) {
+            ret = ff_audio_data_add_to_fifo(avr->out_fifo, fifo_buf, 0,
+                                            fifo_samples);
+            if (ret < 0)
+                goto reinit_fail;
+            ff_audio_data_free(&fifo_buf);
+        }
+    }
+    c = avr->resample;
+    c->compensation_distance = compensation_distance;
+    if (compensation_distance) {
+        c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr *
+                      (int64_t)sample_delta / compensation_distance;
+    } else {
+        c->dst_incr = c->ideal_dst_incr;
+    }
+    return 0;
+
+reinit_fail:
+    ff_audio_data_free(&fifo_buf);
+    return ret;
+}
+
+static int resample(ResampleContext *c, int16_t *dst, const int16_t *src,
+                    int *consumed, int src_size, int dst_size, int update_ctx)
+{
+    int dst_index, i;
+    int index         = c->index;
+    int frac          = c->frac;
+    int dst_incr_frac = c->dst_incr % c->src_incr;
+    int dst_incr      = c->dst_incr / c->src_incr;
+    int compensation_distance = c->compensation_distance;
+
+    if (!dst != !src)
+        return AVERROR(EINVAL);
+
+    if (compensation_distance == 0 && c->filter_length == 1 &&
+        c->phase_shift == 0) {
+        int64_t index2 = ((int64_t)index) << 32;
+        int64_t incr   = (1LL << 32) * c->dst_incr / c->src_incr;
+        dst_size       = FFMIN(dst_size,
+                               (src_size-1-index) * (int64_t)c->src_incr /
+                               c->dst_incr);
+
+        if (dst) {
+            for(dst_index = 0; dst_index < dst_size; dst_index++) {
+                dst[dst_index] = src[index2 >> 32];
+                index2 += incr;
+            }
+        } else {
+            dst_index = dst_size;
+        }
+        index += dst_index * dst_incr;
+        index += (frac + dst_index * (int64_t)dst_incr_frac) / c->src_incr;
+        frac   = (frac + dst_index * (int64_t)dst_incr_frac) % c->src_incr;
+    } else {
+        for (dst_index = 0; dst_index < dst_size; dst_index++) {
+            FELEM *filter = c->filter_bank +
+                            c->filter_length * (index & c->phase_mask);
+            int sample_index = index >> c->phase_shift;
+
+            if (!dst && (sample_index + c->filter_length > src_size ||
+                         -sample_index >= src_size))
+                break;
+
+            if (dst) {
+                FELEM2 val = 0;
+
+                if (sample_index < 0) {
+                    for (i = 0; i < c->filter_length; i++)
+                        val += src[FFABS(sample_index + i) % src_size] *
+                               (FELEM2)filter[i];
+                } else if (sample_index + c->filter_length > src_size) {
+                    break;
+                } else if (c->linear) {
+                    FELEM2 v2 = 0;
+                    for (i = 0; i < c->filter_length; i++) {
+                        val += src[abs(sample_index + i)] * (FELEM2)filter[i];
+                        v2  += src[abs(sample_index + i)] * (FELEM2)filter[i + c->filter_length];
+                    }
+                    val += (v2 - val) * (FELEML)frac / c->src_incr;
+                } else {
+                    for (i = 0; i < c->filter_length; i++)
+                        val += src[sample_index + i] * (FELEM2)filter[i];
+                }
+
+#ifdef CONFIG_RESAMPLE_FLT
+                dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+                val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+                dst[dst_index] = av_clip_int16(val);
+#endif
+            }
+
+            frac  += dst_incr_frac;
+            index += dst_incr;
+            if (frac >= c->src_incr) {
+                frac -= c->src_incr;
+                index++;
+            }
+            if (dst_index + 1 == compensation_distance) {
+                compensation_distance = 0;
+                dst_incr_frac = c->ideal_dst_incr % c->src_incr;
+                dst_incr      = c->ideal_dst_incr / c->src_incr;
+            }
+        }
+    }
+    if (consumed)
+        *consumed = FFMAX(index, 0) >> c->phase_shift;
+
+    if (update_ctx) {
+        if (index >= 0)
+            index &= c->phase_mask;
+
+        if (compensation_distance) {
+            compensation_distance -= dst_index;
+            if (compensation_distance <= 0)
+                return AVERROR_BUG;
+        }
+        c->frac     = frac;
+        c->index    = index;
+        c->dst_incr = dst_incr_frac + c->src_incr*dst_incr;
+        c->compensation_distance = compensation_distance;
+    }
+
+    return dst_index;
+}
+
+int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src,
+                      int *consumed)
+{
+    int ch, in_samples, in_leftover, out_samples = 0;
+    int ret = AVERROR(EINVAL);
+
+    in_samples  = src ? src->nb_samples : 0;
+    in_leftover = c->buffer->nb_samples;
+
+    /* add input samples to the internal buffer */
+    if (src) {
+        ret = ff_audio_data_combine(c->buffer, in_leftover, src, 0, in_samples);
+        if (ret < 0)
+            return ret;
+    } else if (!in_leftover) {
+        /* no remaining samples to flush */
+        return 0;
+    } else {
+        /* TODO: pad buffer to flush completely */
+    }
+
+    /* calculate output size and reallocate output buffer if needed */
+    /* TODO: try to calculate this without the dummy resample() run */
+    if (!dst->read_only && dst->allow_realloc) {
+        out_samples = resample(c, NULL, NULL, NULL, c->buffer->nb_samples,
+                               INT_MAX, 0);
+        ret = ff_audio_data_realloc(dst, out_samples);
+        if (ret < 0) {
+            av_log(c->avr, AV_LOG_ERROR, "error reallocating output\n");
+            return ret;
+        }
+    }
+
+    /* resample each channel plane */
+    for (ch = 0; ch < c->buffer->channels; ch++) {
+        out_samples = resample(c, (int16_t *)dst->data[ch],
+                               (const int16_t *)c->buffer->data[ch], consumed,
+                               c->buffer->nb_samples, dst->allocated_samples,
+                               ch + 1 == c->buffer->channels);
+    }
+    if (out_samples < 0) {
+        av_log(c->avr, AV_LOG_ERROR, "error during resampling\n");
+        return out_samples;
+    }
+
+    /* drain consumed samples from the internal buffer */
+    ff_audio_data_drain(c->buffer, *consumed);
+
+    av_dlog(c->avr, "resampled %d in + %d leftover to %d out + %d leftover\n",
+            in_samples, in_leftover, out_samples, c->buffer->nb_samples);
+
+    dst->nb_samples = out_samples;
+    return 0;
+}
+
+int avresample_get_delay(AVAudioResampleContext *avr)
+{
+    if (!avr->resample_needed || !avr->resample)
+        return 0;
+
+    return avr->resample->buffer->nb_samples;
+}
diff --git a/libavresample/resample.h b/libavresample/resample.h
new file mode 100644 (file)
index 0000000..b42fdbb
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_RESAMPLE_H
+#define AVRESAMPLE_RESAMPLE_H
+
+#include "avresample.h"
+#include "audio_data.h"
+
+typedef struct ResampleContext ResampleContext;
+
+/**
+ * Allocate and initialize a ResampleContext.
+ *
+ * The parameters in the AVAudioResampleContext are used to initialize the
+ * ResampleContext.
+ *
+ * @param avr  AVAudioResampleContext
+ * @return     newly-allocated ResampleContext
+ */
+ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr);
+
+/**
+ * Free a ResampleContext.
+ *
+ * @param c  ResampleContext
+ */
+void ff_audio_resample_free(ResampleContext **c);
+
+/**
+ * Resample audio data.
+ *
+ * Changes the sample rate.
+ *
+ * @par
+ * All samples in the source data may not be consumed depending on the
+ * resampling parameters and the size of the output buffer. The unconsumed
+ * samples are automatically added to the start of the source in the next call.
+ * If the destination data can be reallocated, that may be done in this function
+ * in order to fit all available output. If it cannot be reallocated, fewer
+ * input samples will be consumed in order to have the output fit in the
+ * destination data buffers.
+ *
+ * @param c         ResampleContext
+ * @param dst       destination audio data
+ * @param src       source audio data
+ * @param consumed  number of samples consumed from the source
+ * @return          number of samples written to the destination
+ */
+int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src,
+                      int *consumed);
+
+#endif /* AVRESAMPLE_RESAMPLE_H */
diff --git a/libavresample/utils.c b/libavresample/utils.c
new file mode 100644 (file)
index 0000000..f54dcc6
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/dict.h"
+#include "libavutil/error.h"
+#include "libavutil/log.h"
+#include "libavutil/mem.h"
+#include "libavutil/opt.h"
+
+#include "avresample.h"
+#include "audio_data.h"
+#include "internal.h"
+
+int avresample_open(AVAudioResampleContext *avr)
+{
+    int ret;
+
+    /* set channel mixing parameters */
+    avr->in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout);
+    if (avr->in_channels <= 0 || avr->in_channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(avr, AV_LOG_ERROR, "Invalid input channel layout: %"PRIu64"\n",
+               avr->in_channel_layout);
+        return AVERROR(EINVAL);
+    }
+    avr->out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout);
+    if (avr->out_channels <= 0 || avr->out_channels > AVRESAMPLE_MAX_CHANNELS) {
+        av_log(avr, AV_LOG_ERROR, "Invalid output channel layout: %"PRIu64"\n",
+               avr->out_channel_layout);
+        return AVERROR(EINVAL);
+    }
+    avr->resample_channels = FFMIN(avr->in_channels, avr->out_channels);
+    avr->downmix_needed    = avr->in_channels  > avr->out_channels;
+    avr->upmix_needed      = avr->out_channels > avr->in_channels ||
+                             avr->am->matrix                      ||
+                             (avr->out_channels == avr->in_channels &&
+                              avr->in_channel_layout != avr->out_channel_layout);
+    avr->mixing_needed     = avr->downmix_needed || avr->upmix_needed;
+
+    /* set resampling parameters */
+    avr->resample_needed   = avr->in_sample_rate != avr->out_sample_rate ||
+                             avr->force_resampling;
+
+    /* set sample format conversion parameters */
+    /* override user-requested internal format to avoid unexpected failures
+       TODO: support more internal formats */
+    if (avr->resample_needed && avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P) {
+        av_log(avr, AV_LOG_WARNING, "Using s16p as internal sample format\n");
+        avr->internal_sample_fmt = AV_SAMPLE_FMT_S16P;
+    } else if (avr->mixing_needed &&
+               avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P &&
+               avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) {
+        av_log(avr, AV_LOG_WARNING, "Using fltp as internal sample format\n");
+        avr->internal_sample_fmt = AV_SAMPLE_FMT_FLTP;
+    }
+    if (avr->in_channels == 1)
+        avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt);
+    if (avr->out_channels == 1)
+        avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt);
+    avr->in_convert_needed = (avr->resample_needed || avr->mixing_needed) &&
+                              avr->in_sample_fmt != avr->internal_sample_fmt;
+    if (avr->resample_needed || avr->mixing_needed)
+        avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt;
+    else
+        avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt;
+
+    /* allocate buffers */
+    if (avr->mixing_needed || avr->in_convert_needed) {
+        avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels),
+                                             0, avr->internal_sample_fmt,
+                                             "in_buffer");
+        if (!avr->in_buffer) {
+            ret = AVERROR(EINVAL);
+            goto error;
+        }
+    }
+    if (avr->resample_needed) {
+        avr->resample_out_buffer = ff_audio_data_alloc(avr->out_channels,
+                                                       0, avr->internal_sample_fmt,
+                                                       "resample_out_buffer");
+        if (!avr->resample_out_buffer) {
+            ret = AVERROR(EINVAL);
+            goto error;
+        }
+    }
+    if (avr->out_convert_needed) {
+        avr->out_buffer = ff_audio_data_alloc(avr->out_channels, 0,
+                                              avr->out_sample_fmt, "out_buffer");
+        if (!avr->out_buffer) {
+            ret = AVERROR(EINVAL);
+            goto error;
+        }
+    }
+    avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, avr->out_channels,
+                                        1024);
+    if (!avr->out_fifo) {
+        ret = AVERROR(ENOMEM);
+        goto error;
+    }
+
+    /* setup contexts */
+    if (avr->in_convert_needed) {
+        avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt,
+                                            avr->in_sample_fmt, avr->in_channels);
+        if (!avr->ac_in) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+    }
+    if (avr->out_convert_needed) {
+        enum AVSampleFormat src_fmt;
+        if (avr->in_convert_needed)
+            src_fmt = avr->internal_sample_fmt;
+        else
+            src_fmt = avr->in_sample_fmt;
+        avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt,
+                                             avr->out_channels);
+        if (!avr->ac_out) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+    }
+    if (avr->resample_needed) {
+        avr->resample = ff_audio_resample_init(avr);
+        if (!avr->resample) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+    }
+    if (avr->mixing_needed) {
+        ret = ff_audio_mix_init(avr);
+        if (ret < 0)
+            goto error;
+    }
+
+    return 0;
+
+error:
+    avresample_close(avr);
+    return ret;
+}
+
+void avresample_close(AVAudioResampleContext *avr)
+{
+    ff_audio_data_free(&avr->in_buffer);
+    ff_audio_data_free(&avr->resample_out_buffer);
+    ff_audio_data_free(&avr->out_buffer);
+    av_audio_fifo_free(avr->out_fifo);
+    avr->out_fifo = NULL;
+    av_freep(&avr->ac_in);
+    av_freep(&avr->ac_out);
+    ff_audio_resample_free(&avr->resample);
+    ff_audio_mix_close(avr->am);
+    return;
+}
+
+void avresample_free(AVAudioResampleContext **avr)
+{
+    if (!*avr)
+        return;
+    avresample_close(*avr);
+    av_freep(&(*avr)->am);
+    av_opt_free(*avr);
+    av_freep(avr);
+}
+
+static int handle_buffered_output(AVAudioResampleContext *avr,
+                                  AudioData *output, AudioData *converted)
+{
+    int ret;
+
+    if (!output || av_audio_fifo_size(avr->out_fifo) > 0 ||
+        (converted && output->allocated_samples < converted->nb_samples)) {
+        if (converted) {
+            /* if there are any samples in the output FIFO or if the
+               user-supplied output buffer is not large enough for all samples,
+               we add to the output FIFO */
+            av_dlog(avr, "[FIFO] add %s to out_fifo\n", converted->name);
+            ret = ff_audio_data_add_to_fifo(avr->out_fifo, converted, 0,
+                                            converted->nb_samples);
+            if (ret < 0)
+                return ret;
+        }
+
+        /* if the user specified an output buffer, read samples from the output
+           FIFO to the user output */
+        if (output && output->allocated_samples > 0) {
+            av_dlog(avr, "[FIFO] read from out_fifo to output\n");
+            av_dlog(avr, "[end conversion]\n");
+            return ff_audio_data_read_from_fifo(avr->out_fifo, output,
+                                                output->allocated_samples);
+        }
+    } else if (converted) {
+        /* copy directly to output if it is large enough or there is not any
+           data in the output FIFO */
+        av_dlog(avr, "[copy] %s to output\n", converted->name);
+        output->nb_samples = 0;
+        ret = ff_audio_data_copy(output, converted);
+        if (ret < 0)
+            return ret;
+        av_dlog(avr, "[end conversion]\n");
+        return output->nb_samples;
+    }
+    av_dlog(avr, "[end conversion]\n");
+    return 0;
+}
+
+int avresample_convert(AVAudioResampleContext *avr, void **output,
+                       int out_plane_size, int out_samples, void **input,
+                       int in_plane_size, int in_samples)
+{
+    AudioData input_buffer;
+    AudioData output_buffer;
+    AudioData *current_buffer;
+    int ret;
+
+    /* reset internal buffers */
+    if (avr->in_buffer) {
+        avr->in_buffer->nb_samples = 0;
+        ff_audio_data_set_channels(avr->in_buffer,
+                                   avr->in_buffer->allocated_channels);
+    }
+    if (avr->resample_out_buffer) {
+        avr->resample_out_buffer->nb_samples = 0;
+        ff_audio_data_set_channels(avr->resample_out_buffer,
+                                   avr->resample_out_buffer->allocated_channels);
+    }
+    if (avr->out_buffer) {
+        avr->out_buffer->nb_samples = 0;
+        ff_audio_data_set_channels(avr->out_buffer,
+                                   avr->out_buffer->allocated_channels);
+    }
+
+    av_dlog(avr, "[start conversion]\n");
+
+    /* initialize output_buffer with output data */
+    if (output) {
+        ret = ff_audio_data_init(&output_buffer, output, out_plane_size,
+                                 avr->out_channels, out_samples,
+                                 avr->out_sample_fmt, 0, "output");
+        if (ret < 0)
+            return ret;
+        output_buffer.nb_samples = 0;
+    }
+
+    if (input) {
+        /* initialize input_buffer with input data */
+        ret = ff_audio_data_init(&input_buffer, input, in_plane_size,
+                                 avr->in_channels, in_samples,
+                                 avr->in_sample_fmt, 1, "input");
+        if (ret < 0)
+            return ret;
+        current_buffer = &input_buffer;
+
+        if (avr->upmix_needed && !avr->in_convert_needed && !avr->resample_needed &&
+            !avr->out_convert_needed && output && out_samples >= in_samples) {
+            /* in some rare cases we can copy input to output and upmix
+               directly in the output buffer */
+            av_dlog(avr, "[copy] %s to output\n", current_buffer->name);
+            ret = ff_audio_data_copy(&output_buffer, current_buffer);
+            if (ret < 0)
+                return ret;
+            current_buffer = &output_buffer;
+        } else if (avr->mixing_needed || avr->in_convert_needed) {
+            /* if needed, copy or convert input to in_buffer, and downmix if
+               applicable */
+            if (avr->in_convert_needed) {
+                ret = ff_audio_data_realloc(avr->in_buffer,
+                                            current_buffer->nb_samples);
+                if (ret < 0)
+                    return ret;
+                av_dlog(avr, "[convert] %s to in_buffer\n", current_buffer->name);
+                ret = ff_audio_convert(avr->ac_in, avr->in_buffer, current_buffer,
+                                       current_buffer->nb_samples);
+                if (ret < 0)
+                    return ret;
+            } else {
+                av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name);
+                ret = ff_audio_data_copy(avr->in_buffer, current_buffer);
+                if (ret < 0)
+                    return ret;
+            }
+            ff_audio_data_set_channels(avr->in_buffer, avr->in_channels);
+            if (avr->downmix_needed) {
+                av_dlog(avr, "[downmix] in_buffer\n");
+                ret = ff_audio_mix(avr->am, avr->in_buffer);
+                if (ret < 0)
+                    return ret;
+            }
+            current_buffer = avr->in_buffer;
+        }
+    } else {
+        /* flush resampling buffer and/or output FIFO if input is NULL */
+        if (!avr->resample_needed)
+            return handle_buffered_output(avr, output ? &output_buffer : NULL,
+                                          NULL);
+        current_buffer = NULL;
+    }
+
+    if (avr->resample_needed) {
+        AudioData *resample_out;
+        int consumed = 0;
+
+        if (!avr->out_convert_needed && output && out_samples > 0)
+            resample_out = &output_buffer;
+        else
+            resample_out = avr->resample_out_buffer;
+        av_dlog(avr, "[resample] %s to %s\n", current_buffer->name,
+                resample_out->name);
+        ret = ff_audio_resample(avr->resample, resample_out,
+                                current_buffer, &consumed);
+        if (ret < 0)
+            return ret;
+
+        /* if resampling did not produce any samples, just return 0 */
+        if (resample_out->nb_samples == 0) {
+            av_dlog(avr, "[end conversion]\n");
+            return 0;
+        }
+
+        current_buffer = resample_out;
+    }
+
+    if (avr->upmix_needed) {
+        av_dlog(avr, "[upmix] %s\n", current_buffer->name);
+        ret = ff_audio_mix(avr->am, current_buffer);
+        if (ret < 0)
+            return ret;
+    }
+
+    /* if we resampled or upmixed directly to output, return here */
+    if (current_buffer == &output_buffer) {
+        av_dlog(avr, "[end conversion]\n");
+        return current_buffer->nb_samples;
+    }
+
+    if (avr->out_convert_needed) {
+        if (output && out_samples >= current_buffer->nb_samples) {
+            /* convert directly to output */
+            av_dlog(avr, "[convert] %s to output\n", current_buffer->name);
+            ret = ff_audio_convert(avr->ac_out, &output_buffer, current_buffer,
+                                   current_buffer->nb_samples);
+            if (ret < 0)
+                return ret;
+
+            av_dlog(avr, "[end conversion]\n");
+            return output_buffer.nb_samples;
+        } else {
+            ret = ff_audio_data_realloc(avr->out_buffer,
+                                        current_buffer->nb_samples);
+            if (ret < 0)
+                return ret;
+            av_dlog(avr, "[convert] %s to out_buffer\n", current_buffer->name);
+            ret = ff_audio_convert(avr->ac_out, avr->out_buffer,
+                                   current_buffer, current_buffer->nb_samples);
+            if (ret < 0)
+                return ret;
+            current_buffer = avr->out_buffer;
+        }
+    }
+
+    return handle_buffered_output(avr, &output_buffer, current_buffer);
+}
+
+int avresample_available(AVAudioResampleContext *avr)
+{
+    return av_audio_fifo_size(avr->out_fifo);
+}
+
+int avresample_read(AVAudioResampleContext *avr, void **output, int nb_samples)
+{
+    return av_audio_fifo_read(avr->out_fifo, output, nb_samples);
+}
+
+unsigned avresample_version(void)
+{
+    return LIBAVRESAMPLE_VERSION_INT;
+}
+
+const char *avresample_license(void)
+{
+#define LICENSE_PREFIX "libavresample license: "
+    return LICENSE_PREFIX LIBAV_LICENSE + sizeof(LICENSE_PREFIX) - 1;
+}
+
+const char *avresample_configuration(void)
+{
+    return LIBAV_CONFIGURATION;
+}
diff --git a/libavresample/version.h b/libavresample/version.h
new file mode 100644 (file)
index 0000000..4113edc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVRESAMPLE_VERSION_H
+#define AVRESAMPLE_VERSION_H
+
+#define LIBAVRESAMPLE_VERSION_MAJOR  0
+#define LIBAVRESAMPLE_VERSION_MINOR  0
+#define LIBAVRESAMPLE_VERSION_MICRO  0
+
+#define LIBAVRESAMPLE_VERSION_INT  AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \
+                                                  LIBAVRESAMPLE_VERSION_MINOR, \
+                                                  LIBAVRESAMPLE_VERSION_MICRO)
+#define LIBAVRESAMPLE_VERSION          AV_VERSION(LIBAVRESAMPLE_VERSION_MAJOR, \
+                                                  LIBAVRESAMPLE_VERSION_MINOR, \
+                                                  LIBAVRESAMPLE_VERSION_MICRO)
+#define LIBAVRESAMPLE_BUILD        LIBAVRESAMPLE_VERSION_INT
+
+#define LIBAVRESAMPLE_IDENT        "Lavr" AV_STRINGIFY(LIBAVRESAMPLE_VERSION)
+
+/**
+ * These FF_API_* defines are not part of public API.
+ * They may change, break or disappear at any time.
+ */
+
+#endif /* AVRESAMPLE_VERSION_H */
diff --git a/libavresample/x86/Makefile b/libavresample/x86/Makefile
new file mode 100644 (file)
index 0000000..63697fa
--- /dev/null
@@ -0,0 +1,5 @@
+OBJS      += x86/audio_convert_init.o                                   \
+             x86/audio_mix_init.o
+
+YASM-OBJS += x86/audio_convert.o                                        \
+             x86/audio_mix.o
diff --git a/libavresample/x86/audio_convert.asm b/libavresample/x86/audio_convert.asm
new file mode 100644 (file)
index 0000000..809c5d1
--- /dev/null
@@ -0,0 +1,104 @@
+;******************************************************************************
+;* x86 optimized Format Conversion Utils
+;* Copyright (c) 2008 Loren Merritt
+;*
+;* This file is part of Libav.
+;*
+;* Libav 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.
+;*
+;* Libav 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.
+;*
+;* You should have received a copy of the GNU Lesser General Public
+;* License along with Libav; if not, write to the Free Software
+;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;******************************************************************************
+
+%include "x86inc.asm"
+%include "x86util.asm"
+
+SECTION_TEXT
+
+;-----------------------------------------------------------------------------
+; void ff_conv_fltp_to_flt_6ch(float *dst, float *const *src, int len,
+;                              int channels);
+;-----------------------------------------------------------------------------
+
+%macro CONV_FLTP_TO_FLT_6CH 0
+cglobal conv_fltp_to_flt_6ch, 2,8,7, dst, src, src1, src2, src3, src4, src5, len
+%if ARCH_X86_64
+    mov     lend, r2d
+%else
+    %define lend dword r2m
+%endif
+    mov    src1q, [srcq+1*gprsize]
+    mov    src2q, [srcq+2*gprsize]
+    mov    src3q, [srcq+3*gprsize]
+    mov    src4q, [srcq+4*gprsize]
+    mov    src5q, [srcq+5*gprsize]
+    mov     srcq, [srcq]
+    sub    src1q, srcq
+    sub    src2q, srcq
+    sub    src3q, srcq
+    sub    src4q, srcq
+    sub    src5q, srcq
+.loop:
+    mova      m0, [srcq      ]
+    mova      m1, [srcq+src1q]
+    mova      m2, [srcq+src2q]
+    mova      m3, [srcq+src3q]
+    mova      m4, [srcq+src4q]
+    mova      m5, [srcq+src5q]
+%if cpuflag(sse)
+    SBUTTERFLYPS 0, 1, 6
+    SBUTTERFLYPS 2, 3, 6
+    SBUTTERFLYPS 4, 5, 6
+
+    movaps    m6, m4
+    shufps    m4, m0, q3210
+    movlhps   m0, m2
+    movhlps   m6, m2
+    movaps [dstq   ], m0
+    movaps [dstq+16], m4
+    movaps [dstq+32], m6
+
+    movaps    m6, m5
+    shufps    m5, m1, q3210
+    movlhps   m1, m3
+    movhlps   m6, m3
+    movaps [dstq+48], m1
+    movaps [dstq+64], m5
+    movaps [dstq+80], m6
+%else ; mmx
+    SBUTTERFLY dq, 0, 1, 6
+    SBUTTERFLY dq, 2, 3, 6
+    SBUTTERFLY dq, 4, 5, 6
+
+    movq   [dstq   ], m0
+    movq   [dstq+ 8], m2
+    movq   [dstq+16], m4
+    movq   [dstq+24], m1
+    movq   [dstq+32], m3
+    movq   [dstq+40], m5
+%endif
+    add      srcq, mmsize
+    add      dstq, mmsize*6
+    sub      lend, mmsize/4
+    jg .loop
+%if mmsize == 8
+    emms
+    RET
+%else
+    REP_RET
+%endif
+%endmacro
+
+INIT_MMX mmx
+CONV_FLTP_TO_FLT_6CH
+INIT_XMM sse
+CONV_FLTP_TO_FLT_6CH
diff --git a/libavresample/x86/audio_convert_init.c b/libavresample/x86/audio_convert_init.c
new file mode 100644 (file)
index 0000000..6883f10
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/cpu.h"
+#include "libavresample/audio_convert.h"
+
+extern void ff_conv_fltp_to_flt_6ch_mmx(float *dst, float *const *src, int len);
+extern void ff_conv_fltp_to_flt_6ch_sse(float *dst, float *const *src, int len);
+
+av_cold void ff_audio_convert_init_x86(AudioConvert *ac)
+{
+#if HAVE_YASM
+    int mm_flags = av_get_cpu_flags();
+
+    if (mm_flags & AV_CPU_FLAG_MMX && HAVE_MMX) {
+        ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
+                                  6, 1, 4, "MMX", ff_conv_fltp_to_flt_6ch_mmx);
+    }
+    if (mm_flags & AV_CPU_FLAG_SSE && HAVE_SSE) {
+        ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP,
+                                  6, 16, 4, "SSE", ff_conv_fltp_to_flt_6ch_sse);
+    }
+#endif
+}
diff --git a/libavresample/x86/audio_mix.asm b/libavresample/x86/audio_mix.asm
new file mode 100644 (file)
index 0000000..ef30f02
--- /dev/null
@@ -0,0 +1,64 @@
+;******************************************************************************
+;* x86 optimized channel mixing
+;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+;*
+;* This file is part of Libav.
+;*
+;* Libav 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.
+;*
+;* Libav 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.
+;*
+;* You should have received a copy of the GNU Lesser General Public
+;* License along with Libav; if not, write to the Free Software
+;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+;******************************************************************************
+
+%include "x86inc.asm"
+%include "x86util.asm"
+
+SECTION_TEXT
+
+;-----------------------------------------------------------------------------
+; void ff_mix_2_to_1_fltp_flt(float **src, float **matrix, int len,
+;                             int out_ch, int in_ch);
+;-----------------------------------------------------------------------------
+
+%macro MIX_2_TO_1_FLTP_FLT 0
+cglobal mix_2_to_1_fltp_flt, 3,4,6, src, matrix, len, src1
+    mov       src1q, [srcq+gprsize]
+    mov        srcq, [srcq        ]
+    sub       src1q, srcq
+    mov     matrixq, [matrixq  ]
+    VBROADCASTSS m4, [matrixq  ]
+    VBROADCASTSS m5, [matrixq+4]
+    ALIGN 16
+.loop:
+    mulps        m0, m4, [srcq             ]
+    mulps        m1, m5, [srcq+src1q       ]
+    mulps        m2, m4, [srcq+      mmsize]
+    mulps        m3, m5, [srcq+src1q+mmsize]
+    addps        m0, m0, m1
+    addps        m2, m2, m3
+    mova  [srcq       ], m0
+    mova  [srcq+mmsize], m2
+    add        srcq, mmsize*2
+    sub        lend, mmsize*2/4
+    jg .loop
+%if mmsize == 32
+    vzeroupper
+    RET
+%else
+    REP_RET
+%endif
+%endmacro
+
+INIT_XMM sse
+MIX_2_TO_1_FLTP_FLT
+INIT_YMM avx
+MIX_2_TO_1_FLTP_FLT
diff --git a/libavresample/x86/audio_mix_init.c b/libavresample/x86/audio_mix_init.c
new file mode 100644 (file)
index 0000000..8f8930f
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com>
+ *
+ * This file is part of Libav.
+ *
+ * Libav 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.
+ *
+ * Libav 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.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Libav; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/cpu.h"
+#include "libavresample/audio_mix.h"
+
+extern void ff_mix_2_to_1_fltp_flt_sse(float **src, float **matrix, int len,
+                                       int out_ch, int in_ch);
+extern void ff_mix_2_to_1_fltp_flt_avx(float **src, float **matrix, int len,
+                                       int out_ch, int in_ch);
+
+av_cold void ff_audio_mix_init_x86(AudioMix *am)
+{
+#if HAVE_YASM
+    int mm_flags = av_get_cpu_flags();
+
+    if (mm_flags & AV_CPU_FLAG_SSE && HAVE_SSE) {
+        ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                              2, 1, 16, 8, "SSE", ff_mix_2_to_1_fltp_flt_sse);
+    }
+    if (mm_flags & AV_CPU_FLAG_AVX && HAVE_AVX) {
+        ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,
+                              2, 1, 32, 16, "AVX", ff_mix_2_to_1_fltp_flt_avx);
+    }
+#endif
+}
index a9c7564..55f4a93 100644 (file)
     pminsd  %1, %3
     pmaxsd  %1, %2
 %endmacro
+
+%macro VBROADCASTSS 2 ; dst xmm/ymm, src m32
+%if cpuflag(avx)
+    vbroadcastss %1, %2
+%else ; sse
+    movss        %1, %2
+    shufps       %1, %1, 0
+%endif
+%endmacro