Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add libavresample

This is a new library for audio sample format, channel layout, and sample rate
conversion.
  • Loading branch information...
commit c8af852b97447491823ff9b91413e32415e2babf 1 parent c5671ae
@justinruggles justinruggles authored
View
1  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:
View
3  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
View
7 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"
View
16 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()
View
15 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
View
334 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;
+}
View
87 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 */
View
345 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;
+}
View
173 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 */
View
356 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;
+}
View
108 libavresample/audio_mix.h
@@ -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 */
View
346 libavresample/audio_mix_matrix.c
@@ -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;
+}
View
340 libavresample/avresample-test.c
@@ -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;
+}
View
283 libavresample/avresample.h
@@ -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 */
View
75 libavresample/internal.h
@@ -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 */
View
4 libavresample/libavresample.v
@@ -0,0 +1,4 @@
+LIBAVRESAMPLE_$MAJOR {
+ global: av*;
+ local: *;
+};
View
89 libavresample/options.c
@@ -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[] = {