Browse Source

Add libavresample

This is a new library for audio sample format, channel layout, and sample rate
conversion.
Justin Ruggles 13 years ago
parent
commit
c8af852b97

+ 1 - 0
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:

+ 2 - 1
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
 

+ 6 - 1
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"

+ 10 - 6
doc/APIchanges

@@ -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()

+ 15 - 0
libavresample/Makefile

@@ -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

+ 334 - 0
libavresample/audio_convert.c

@@ -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;
+}

+ 87 - 0
libavresample/audio_convert.h

@@ -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 */

+ 345 - 0
libavresample/audio_data.c

@@ -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;
+}

+ 173 - 0
libavresample/audio_data.h

@@ -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 */

+ 356 - 0
libavresample/audio_mix.c

@@ -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;
+}

Some files were not shown because too many files changed in this diff