summaryrefslogtreecommitdiff
path: root/libswresample
diff options
context:
space:
mode:
Diffstat (limited to 'libswresample')
-rw-r--r--libswresample/Makefile10
-rw-r--r--libswresample/audioconvert.c158
-rw-r--r--libswresample/audioconvert.h68
-rw-r--r--libswresample/libswresample.v4
-rw-r--r--libswresample/rematrix.c295
-rw-r--r--libswresample/rematrix_template.c38
-rw-r--r--libswresample/resample.c352
-rw-r--r--libswresample/swresample.c505
-rw-r--r--libswresample/swresample.h125
-rw-r--r--libswresample/swresample_internal.h88
-rw-r--r--libswresample/swresample_test.c216
11 files changed, 1859 insertions, 0 deletions
diff --git a/libswresample/Makefile b/libswresample/Makefile
new file mode 100644
index 0000000000..6393598ab7
--- /dev/null
+++ b/libswresample/Makefile
@@ -0,0 +1,10 @@
+include $(SUBDIR)../config.mak
+
+NAME = swresample
+FFLIBS = avutil
+
+HEADERS = swresample.h
+
+OBJS = swresample.o audioconvert.o resample.o rematrix.o
+
+TESTPROGS = swresample_test
diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c
new file mode 100644
index 0000000000..5957139447
--- /dev/null
+++ b/libswresample/audioconvert.c
@@ -0,0 +1,158 @@
+/*
+ * audio conversion
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * audio conversion
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "libavutil/avstring.h"
+#include "libavutil/avassert.h"
+#include "libavutil/libm.h"
+#include "libavutil/samplefmt.h"
+#include "audioconvert.h"
+
+
+typedef void (conv_func_type)(uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end);
+
+struct AudioConvert {
+ int channels;
+ conv_func_type *conv_f;
+ const int *ch_map;
+ uint8_t silence[8]; ///< silence input sample
+};
+
+#define CONV_FUNC_NAME(dst_fmt, src_fmt) conv_ ## src_fmt ## _to_ ## dst_fmt
+
+//FIXME rounding ?
+#define CONV_FUNC(ofmt, otype, ifmt, expr)\
+static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end)\
+{\
+ do{\
+ *(otype*)po = expr; pi += is; po += os;\
+ }while(po < end);\
+}
+
+//FIXME put things below under ifdefs so we do not waste space for cases no codec will need
+CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_U8 , *(const uint8_t*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<8)
+CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)<<24)
+CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7)))
+CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_U8 , (*(const uint8_t*)pi - 0x80)*(1.0 / (1<<7)))
+CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S16, (*(const int16_t*)pi>>8) + 0x80)
+CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, *(const int16_t*)pi<<16)
+CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15)))
+CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S16, *(const int16_t*)pi*(1.0 / (1<<15)))
+CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_S32, (*(const int32_t*)pi>>24) + 0x80)
+CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi>>16)
+CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, *(const int32_t*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31)))
+CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_S32, *(const int32_t*)pi*(1.0 / (1U<<31)))
+CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_FLT, av_clip_uint8( lrintf(*(const float*)pi * (1<<7)) + 0x80))
+CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, av_clip_int16( lrintf(*(const float*)pi * (1<<15))))
+CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, av_clipl_int32(llrintf(*(const float*)pi * (1U<<31))))
+CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_FLT, *(const float*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_FLT, *(const float*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_U8 , uint8_t, AV_SAMPLE_FMT_DBL, av_clip_uint8( lrint(*(const double*)pi * (1<<7)) + 0x80))
+CONV_FUNC(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, av_clip_int16( lrint(*(const double*)pi * (1<<15))))
+CONV_FUNC(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, av_clipl_int32(llrint(*(const double*)pi * (1U<<31))))
+CONV_FUNC(AV_SAMPLE_FMT_FLT, float , AV_SAMPLE_FMT_DBL, *(const double*)pi)
+CONV_FUNC(AV_SAMPLE_FMT_DBL, double , AV_SAMPLE_FMT_DBL, *(const double*)pi)
+
+#define FMT_PAIR_FUNC(out, in) [out + AV_SAMPLE_FMT_NB*in] = CONV_FUNC_NAME(out, in)
+
+conv_func_type *fmt_pair_to_conv_functions[AV_SAMPLE_FMT_NB*AV_SAMPLE_FMT_NB] = {
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8 , AV_SAMPLE_FMT_U8 ),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8 ),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_U8 ),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_U8 ),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8 ),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8 , AV_SAMPLE_FMT_S16),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S16),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8 , AV_SAMPLE_FMT_S32),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S32),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8 , AV_SAMPLE_FMT_FLT),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_FLT),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_U8 , AV_SAMPLE_FMT_DBL),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DBL),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL),
+ FMT_PAIR_FUNC(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL),
+};
+
+AudioConvert *swri_audio_convert_alloc(enum AVSampleFormat out_fmt,
+ enum AVSampleFormat in_fmt,
+ int channels, const int *ch_map,
+ int flags)
+{
+ AudioConvert *ctx;
+ conv_func_type *f = fmt_pair_to_conv_functions[out_fmt + AV_SAMPLE_FMT_NB*in_fmt];
+
+ if (!f)
+ return NULL;
+ ctx = av_mallocz(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+ ctx->channels = channels;
+ ctx->conv_f = f;
+ ctx->ch_map = ch_map;
+ if (in_fmt == AV_SAMPLE_FMT_U8)
+ memset(ctx->silence, 0x80, sizeof(ctx->silence));
+ return ctx;
+}
+
+void swri_audio_convert_free(AudioConvert **ctx)
+{
+ av_freep(ctx);
+}
+
+int swri_audio_convert(AudioConvert *ctx, AudioData *out, AudioData *in, int len)
+{
+ int ch;
+
+ av_assert0(ctx->channels == out->ch_count);
+
+ //FIXME optimize common cases
+
+ for(ch=0; ch<ctx->channels; ch++){
+ const int ich= ctx->ch_map ? ctx->ch_map[ch] : ch;
+ const int is= ich < 0 ? 0 : (in->planar ? 1 : in->ch_count) * in->bps;
+ const int os= (out->planar ? 1 :out->ch_count) *out->bps;
+ const uint8_t *pi= ich < 0 ? ctx->silence : in->ch[ich];
+ uint8_t *po= out->ch[ch];
+ uint8_t *end= po + os*len;
+ if(!po)
+ continue;
+ ctx->conv_f(po, pi, is, os, end);
+ }
+ return 0;
+}
diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h
new file mode 100644
index 0000000000..6d09271b35
--- /dev/null
+++ b/libswresample/audioconvert.h
@@ -0,0 +1,68 @@
+/*
+ * audio conversion
+ * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
+ * Copyright (c) 2008 Peter Ross
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWR_AUDIOCONVERT_H
+#define SWR_AUDIOCONVERT_H
+
+/**
+ * @file
+ * Audio format conversion routines
+ */
+
+
+#include "swresample_internal.h"
+#include "libavutil/cpu.h"
+#include "libavutil/audioconvert.h"
+
+struct AudioConvert;
+typedef struct AudioConvert AudioConvert;
+
+/**
+ * Create an audio sample format converter context
+ * @param out_fmt Output sample format
+ * @param in_fmt Input sample format
+ * @param channels Number of channels
+ * @param flags See AV_CPU_FLAG_xx
+ * @param ch_map list of the channels id to pick from the source stream, NULL
+ * if all channels must be selected
+ * @return NULL on error
+ */
+AudioConvert *swri_audio_convert_alloc(enum AVSampleFormat out_fmt,
+ enum AVSampleFormat in_fmt,
+ int channels, const int *ch_map,
+ int flags);
+
+/**
+ * Free audio sample format converter context.
+ * and set the pointer to NULL
+ */
+void swri_audio_convert_free(AudioConvert **ctx);
+
+/**
+ * Convert between audio sample formats
+ * @param[in] out array of output buffers for each channel. set to NULL to ignore processing of the given channel.
+ * @param[in] in array of input buffers for each channel
+ * @param len length of audio frame size (measured in samples)
+ */
+int swri_audio_convert(AudioConvert *ctx, AudioData *out, AudioData *in, int len);
+
+#endif /* AUDIOCONVERT_H */
diff --git a/libswresample/libswresample.v b/libswresample/libswresample.v
new file mode 100644
index 0000000000..af2181179d
--- /dev/null
+++ b/libswresample/libswresample.v
@@ -0,0 +1,4 @@
+LIBSWRESAMPLE_$MAJOR {
+ global: swr_*; ff_*;
+ local: *;
+};
diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c
new file mode 100644
index 0000000000..0e4d9630c8
--- /dev/null
+++ b/libswresample/rematrix.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample 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.
+ *
+ * libswresample 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 libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "swresample_internal.h"
+#include "libavutil/audioconvert.h"
+#include "libavutil/avassert.h"
+
+#define ONE (1.0)
+#define R(x) x
+#define SAMPLE float
+#define COEFF float
+#define RENAME(x) x ## _float
+#include "rematrix_template.c"
+#undef SAMPLE
+#undef RENAME
+#undef R
+#undef ONE
+#undef COEFF
+
+#define ONE (-32768)
+#define R(x) (((x) + 16384)>>15)
+#define SAMPLE int16_t
+#define COEFF int
+#define RENAME(x) x ## _s16
+#include "rematrix_template.c"
+
+
+#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
+
+static int even(int64_t layout){
+ if(!layout) return 1;
+ if(layout&(layout-1)) return 1;
+ return 0;
+}
+
+static int sane_layout(int64_t layout){
+ if(!(layout & AV_CH_LAYOUT_SURROUND)) // at least 1 front speaker
+ return 0;
+ if(!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT))) // no asymetric front
+ return 0;
+ if(!even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT))) // no asymetric side
+ return 0;
+ if(!even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT)))
+ return 0;
+ if(!even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)))
+ return 0;
+ if(av_get_channel_layout_nb_channels(layout) >= SWR_CH_MAX)
+ return 0;
+
+ return 1;
+}
+
+int swri_rematrix_init(SwrContext *s){
+ int i, j, out_i;
+ double matrix[64][64]={{0}};
+ int64_t unaccounted= s->in_ch_layout & ~s->out_ch_layout;
+ double maxcoef=0;
+
+ for(i=0; i<64; i++){
+ if(s->in_ch_layout & s->out_ch_layout & (1LL<<i))
+ matrix[i][i]= 1.0;
+ }
+
+ if(!sane_layout(s->in_ch_layout)){
+ av_log(s, AV_LOG_ERROR, "Input channel layout isnt supported\n");
+ return AVERROR(EINVAL);
+ }
+ if(!sane_layout(s->out_ch_layout)){
+ av_log(s, AV_LOG_ERROR, "Output channel layout isnt supported\n");
+ return AVERROR(EINVAL);
+ }
+
+//FIXME implement dolby surround
+//FIXME implement full ac3
+
+
+ if(unaccounted & AV_CH_FRONT_CENTER){
+ if((s->out_ch_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
+ av_assert0(0);
+ }
+ if(unaccounted & AV_CH_LAYOUT_STEREO){
+ if(s->out_ch_layout & AV_CH_FRONT_CENTER){
+ matrix[FRONT_CENTER][ FRONT_LEFT]+= M_SQRT1_2;
+ matrix[FRONT_CENTER][FRONT_RIGHT]+= M_SQRT1_2;
+ if(s->in_ch_layout & AV_CH_FRONT_CENTER)
+ matrix[FRONT_CENTER][ FRONT_CENTER] = s->clev*sqrt(2);
+ }else
+ av_assert0(0);
+ }
+
+ if(unaccounted & AV_CH_BACK_CENTER){
+ if(s->out_ch_layout & AV_CH_BACK_LEFT){
+ matrix[ BACK_LEFT][BACK_CENTER]+= M_SQRT1_2;
+ matrix[BACK_RIGHT][BACK_CENTER]+= M_SQRT1_2;
+ }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){
+ matrix[ SIDE_LEFT][BACK_CENTER]+= M_SQRT1_2;
+ matrix[SIDE_RIGHT][BACK_CENTER]+= M_SQRT1_2;
+ }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){
+ matrix[ FRONT_LEFT][BACK_CENTER]+= s->slev*M_SQRT1_2;
+ matrix[FRONT_RIGHT][BACK_CENTER]+= s->slev*M_SQRT1_2;
+ }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){
+ matrix[ FRONT_CENTER][BACK_CENTER]+= s->slev*M_SQRT1_2;
+ }else
+ av_assert0(0);
+ }
+ if(unaccounted & AV_CH_BACK_LEFT){
+ if(s->out_ch_layout & AV_CH_BACK_CENTER){
+ matrix[BACK_CENTER][ BACK_LEFT]+= M_SQRT1_2;
+ matrix[BACK_CENTER][BACK_RIGHT]+= M_SQRT1_2;
+ }else if(s->out_ch_layout & AV_CH_SIDE_LEFT){
+ if(s->in_ch_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(s->out_ch_layout & AV_CH_FRONT_LEFT){
+ matrix[ FRONT_LEFT][ BACK_LEFT]+= s->slev;
+ matrix[FRONT_RIGHT][BACK_RIGHT]+= s->slev;
+ }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){
+ matrix[ FRONT_CENTER][BACK_LEFT ]+= s->slev*M_SQRT1_2;
+ matrix[ FRONT_CENTER][BACK_RIGHT]+= s->slev*M_SQRT1_2;
+ }else
+ av_assert0(0);
+ }
+
+ if(unaccounted & AV_CH_SIDE_LEFT){
+ if(s->out_ch_layout & AV_CH_BACK_LEFT){
+ matrix[ BACK_LEFT][ SIDE_LEFT]+= 1.0;
+ matrix[BACK_RIGHT][SIDE_RIGHT]+= 1.0;
+ }else if(s->out_ch_layout & AV_CH_BACK_CENTER){
+ matrix[BACK_CENTER][ SIDE_LEFT]+= M_SQRT1_2;
+ matrix[BACK_CENTER][SIDE_RIGHT]+= M_SQRT1_2;
+ }else if(s->out_ch_layout & AV_CH_FRONT_LEFT){
+ matrix[ FRONT_LEFT][ SIDE_LEFT]+= s->slev;
+ matrix[FRONT_RIGHT][SIDE_RIGHT]+= s->slev;
+ }else if(s->out_ch_layout & AV_CH_FRONT_CENTER){
+ matrix[ FRONT_CENTER][SIDE_LEFT ]+= s->slev*M_SQRT1_2;
+ matrix[ FRONT_CENTER][SIDE_RIGHT]+= s->slev*M_SQRT1_2;
+ }else
+ av_assert0(0);
+ }
+
+ if(unaccounted & AV_CH_FRONT_LEFT_OF_CENTER){
+ if(s->out_ch_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(s->out_ch_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
+ av_assert0(0);
+ }
+
+ //FIXME quantize for integeres
+ for(out_i=i=0; i<64; i++){
+ double sum=0;
+ int in_i=0;
+ int ch_in=0;
+ for(j=0; j<64; j++){
+ s->matrix[out_i][in_i]= matrix[i][j];
+ s->matrix32[out_i][in_i]= lrintf(matrix[i][j] * 32768);
+ if(matrix[i][j]){
+ s->matrix_ch[out_i][++ch_in]= in_i;
+ sum += fabs(matrix[i][j]);
+ }
+ if(s->in_ch_layout & (1ULL<<j))
+ in_i++;
+ }
+ s->matrix_ch[out_i][0]= ch_in;
+ maxcoef= FFMAX(maxcoef, sum);
+ if(s->out_ch_layout & (1ULL<<i))
+ out_i++;
+ }
+ if(s->rematrix_volume < 0)
+ maxcoef = -s->rematrix_volume;
+
+ if(( s->out_sample_fmt < AV_SAMPLE_FMT_FLT
+ || s->int_sample_fmt < AV_SAMPLE_FMT_FLT) && maxcoef > 1.0){
+ for(i=0; i<SWR_CH_MAX; i++)
+ for(j=0; j<SWR_CH_MAX; j++){
+ s->matrix[i][j] /= maxcoef;
+ s->matrix32[i][j]= lrintf(s->matrix[i][j] * 32768);
+ }
+ }
+
+ if(s->rematrix_volume > 0){
+ for(i=0; i<SWR_CH_MAX; i++)
+ for(j=0; j<SWR_CH_MAX; j++){
+ s->matrix[i][j] *= s->rematrix_volume;
+ s->matrix32[i][j]= lrintf(s->matrix[i][j] * 32768);
+ }
+ }
+
+ for(i=0; i<av_get_channel_layout_nb_channels(s->out_ch_layout); i++){
+ for(j=0; j<av_get_channel_layout_nb_channels(s->in_ch_layout); j++){
+ av_log(NULL, AV_LOG_DEBUG, "%f ", s->matrix[i][j]);
+ }
+ av_log(NULL, AV_LOG_DEBUG, "\n");
+ }
+ return 0;
+}
+
+int swri_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy){
+ int out_i, in_i, i, j;
+
+ av_assert0(out->ch_count == av_get_channel_layout_nb_channels(s->out_ch_layout));
+ av_assert0(in ->ch_count == av_get_channel_layout_nb_channels(s-> in_ch_layout));
+
+ for(out_i=0; out_i<out->ch_count; out_i++){
+ switch(s->matrix_ch[out_i][0]){
+ case 1:
+ in_i= s->matrix_ch[out_i][1];
+ if(mustcopy || s->matrix[out_i][in_i]!=1.0){
+ if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){
+ copy_float((float *)out->ch[out_i], (const float *)in->ch[in_i], s->matrix [out_i][in_i], len);
+ }else
+ copy_s16 ((int16_t*)out->ch[out_i], (const int16_t*)in->ch[in_i], s->matrix32[out_i][in_i], len);
+ }else{
+ out->ch[out_i]= in->ch[in_i];
+ }
+ break;
+ case 2:
+ if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){
+ sum2_float((float *)out->ch[out_i], (const float *)in->ch[ s->matrix_ch[out_i][1] ], (const float *)in->ch[ s->matrix_ch[out_i][2] ],
+ s->matrix[out_i][ s->matrix_ch[out_i][1] ], s->matrix[out_i][ s->matrix_ch[out_i][2] ],
+ len);
+ }else{
+ sum2_s16 ((int16_t*)out->ch[out_i], (const int16_t*)in->ch[ s->matrix_ch[out_i][1] ], (const int16_t*)in->ch[ s->matrix_ch[out_i][2] ],
+ s->matrix32[out_i][ s->matrix_ch[out_i][1] ], s->matrix32[out_i][ s->matrix_ch[out_i][2] ],
+ len);
+ }
+ break;
+ default:
+ if(s->int_sample_fmt == AV_SAMPLE_FMT_FLT){
+ for(i=0; i<len; i++){
+ float v=0;
+ for(j=0; j<s->matrix_ch[out_i][0]; j++){
+ in_i= s->matrix_ch[out_i][1+j];
+ v+= ((float*)in->ch[in_i])[i] * s->matrix[out_i][in_i];
+ }
+ ((float*)out->ch[out_i])[i]= v;
+ }
+ }else{
+ for(i=0; i<len; i++){
+ int v=0;
+ for(j=0; j<s->matrix_ch[out_i][0]; j++){
+ in_i= s->matrix_ch[out_i][1+j];
+ v+= ((int16_t*)in->ch[in_i])[i] * s->matrix32[out_i][in_i];
+ }
+ ((int16_t*)out->ch[out_i])[i]= (v + 16384)>>15;
+ }
+ }
+ }
+ }
+ return 0;
+}
diff --git a/libswresample/rematrix_template.c b/libswresample/rematrix_template.c
new file mode 100644
index 0000000000..862430e184
--- /dev/null
+++ b/libswresample/rematrix_template.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample 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.
+ *
+ * libswresample 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 libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+static void RENAME(sum2)(SAMPLE *out, const SAMPLE *in1, const SAMPLE *in2, COEFF coeff1, COEFF coeff2, int len){
+ int i;
+
+ for(i=0; i<len; i++)
+ out[i] = R(coeff1*in1[i] + coeff2*in2[i]);
+}
+
+static void RENAME(copy)(SAMPLE *out, const SAMPLE *in, COEFF coeff, int len){
+ if(coeff == ONE){
+ memcpy(out, in, sizeof(SAMPLE)*len);
+ }else{
+ int i;
+ for(i=0; i<len; i++)
+ out[i] = R(coeff*in[i]);
+ }
+}
+
diff --git a/libswresample/resample.c b/libswresample/resample.c
new file mode 100644
index 0000000000..9b582eac84
--- /dev/null
+++ b/libswresample/resample.c
@@ -0,0 +1,352 @@
+/*
+ * audio resampling
+ * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * audio resampling
+ * @author Michael Niedermayer <michaelni@gmx.at>
+ */
+
+#include "libavutil/log.h"
+#include "swresample_internal.h"
+
+#ifndef CONFIG_RESAMPLE_HP
+#define FILTER_SHIFT 15
+
+#define FELEM int16_t
+#define FELEM2 int32_t
+#define FELEML int64_t
+#define FELEM_MAX INT16_MAX
+#define FELEM_MIN INT16_MIN
+#define WINDOW_TYPE 9
+#elif !defined(CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE)
+#define FILTER_SHIFT 30
+
+#define FELEM int32_t
+#define FELEM2 int64_t
+#define FELEML int64_t
+#define FELEM_MAX INT32_MAX
+#define FELEM_MIN INT32_MIN
+#define WINDOW_TYPE 12
+#else
+#define FILTER_SHIFT 0
+
+#define FELEM double
+#define FELEM2 double
+#define FELEML double
+#define WINDOW_TYPE 24
+#endif
+
+
+typedef struct ResampleContext {
+ const AVClass *av_class;
+ FELEM *filter_bank;
+ int filter_length;
+ int ideal_dst_incr;
+ int dst_incr;
+ int index;
+ int frac;
+ int src_incr;
+ int compensation_distance;
+ int phase_shift;
+ int phase_mask;
+ int linear;
+ double factor;
+} ResampleContext;
+
+/**
+ * 0th order modified bessel function of the first kind.
+ */
+static double bessel(double x){
+ double v=1;
+ double lastv=0;
+ double t=1;
+ int i;
+ static const double inv[100]={
+ 1.0/( 1* 1), 1.0/( 2* 2), 1.0/( 3* 3), 1.0/( 4* 4), 1.0/( 5* 5), 1.0/( 6* 6), 1.0/( 7* 7), 1.0/( 8* 8), 1.0/( 9* 9), 1.0/(10*10),
+ 1.0/(11*11), 1.0/(12*12), 1.0/(13*13), 1.0/(14*14), 1.0/(15*15), 1.0/(16*16), 1.0/(17*17), 1.0/(18*18), 1.0/(19*19), 1.0/(20*20),
+ 1.0/(21*21), 1.0/(22*22), 1.0/(23*23), 1.0/(24*24), 1.0/(25*25), 1.0/(26*26), 1.0/(27*27), 1.0/(28*28), 1.0/(29*29), 1.0/(30*30),
+ 1.0/(31*31), 1.0/(32*32), 1.0/(33*33), 1.0/(34*34), 1.0/(35*35), 1.0/(36*36), 1.0/(37*37), 1.0/(38*38), 1.0/(39*39), 1.0/(40*40),
+ 1.0/(41*41), 1.0/(42*42), 1.0/(43*43), 1.0/(44*44), 1.0/(45*45), 1.0/(46*46), 1.0/(47*47), 1.0/(48*48), 1.0/(49*49), 1.0/(50*50),
+ 1.0/(51*51), 1.0/(52*52), 1.0/(53*53), 1.0/(54*54), 1.0/(55*55), 1.0/(56*56), 1.0/(57*57), 1.0/(58*58), 1.0/(59*59), 1.0/(60*60),
+ 1.0/(61*61), 1.0/(62*62), 1.0/(63*63), 1.0/(64*64), 1.0/(65*65), 1.0/(66*66), 1.0/(67*67), 1.0/(68*68), 1.0/(69*69), 1.0/(70*70),
+ 1.0/(71*71), 1.0/(72*72), 1.0/(73*73), 1.0/(74*74), 1.0/(75*75), 1.0/(76*76), 1.0/(77*77), 1.0/(78*78), 1.0/(79*79), 1.0/(80*80),
+ 1.0/(81*81), 1.0/(82*82), 1.0/(83*83), 1.0/(84*84), 1.0/(85*85), 1.0/(86*86), 1.0/(87*87), 1.0/(88*88), 1.0/(89*89), 1.0/(90*90),
+ 1.0/(91*91), 1.0/(92*92), 1.0/(93*93), 1.0/(94*94), 1.0/(95*95), 1.0/(96*96), 1.0/(97*97), 1.0/(98*98), 1.0/(99*99), 1.0/(10000)
+ };
+
+ x= x*x/4;
+ for(i=0; v != lastv; i++){
+ lastv=v;
+ t *= x*inv[i];
+ v += t;
+ }
+ return v;
+}
+
+/**
+ * builds a polyphase filterbank.
+ * @param factor resampling factor
+ * @param scale wanted sum of coefficients for each filter
+ * @param type 0->cubic, 1->blackman nuttall windowed sinc, 2..16->kaiser windowed sinc beta=2..16
+ * @return 0 on success, negative on error
+ */
+static int build_filter(FELEM *filter, double factor, int tap_count, int phase_count, int scale, int type){
+ int ph, i;
+ double x, y, w;
+ double *tab = av_malloc(tap_count * sizeof(*tab));
+ const int center= (tap_count-1)/2;
+
+ if (!tab)
+ return AVERROR(ENOMEM);
+
+ /* if upsampling, only need to interpolate, no filter */
+ if (factor > 1.0)
+ factor = 1.0;
+
+ for(ph=0;ph<phase_count;ph++) {
+ double norm = 0;
+ for(i=0;i<tap_count;i++) {
+ x = M_PI * ((double)(i - center) - (double)ph / phase_count) * factor;
+ if (x == 0) y = 1.0;
+ else y = sin(x) / x;
+ switch(type){
+ case 0:{
+ const float d= -0.5; //first order derivative = -0.5
+ x = fabs(((double)(i - center) - (double)ph / phase_count) * factor);
+ if(x<1.0) y= 1 - 3*x*x + 2*x*x*x + d*( -x*x + x*x*x);
+ else y= d*(-4 + 8*x - 5*x*x + x*x*x);
+ break;}
+ case 1:
+ w = 2.0*x / (factor*tap_count) + M_PI;
+ y *= 0.3635819 - 0.4891775 * cos(w) + 0.1365995 * cos(2*w) - 0.0106411 * cos(3*w);
+ break;
+ default:
+ w = 2.0*x / (factor*tap_count*M_PI);
+ y *= bessel(type*sqrt(FFMAX(1-w*w, 0)));
+ break;
+ }
+
+ tab[i] = y;
+ norm += y;
+ }
+
+ /* normalize so that an uniform color remains the same */
+ for(i=0;i<tap_count;i++) {
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ filter[ph * tap_count + i] = tab[i] / norm;
+#else
+ filter[ph * tap_count + i] = av_clip(lrintf(tab[i] * scale / norm), FELEM_MIN, FELEM_MAX);
+#endif
+ }
+ }
+#if 0
+ {
+#define LEN 1024
+ int j,k;
+ double sine[LEN + tap_count];
+ double filtered[LEN];
+ double maxff=-2, minff=2, maxsf=-2, minsf=2;
+ for(i=0; i<LEN; i++){
+ double ss=0, sf=0, ff=0;
+ for(j=0; j<LEN+tap_count; j++)
+ sine[j]= cos(i*j*M_PI/LEN);
+ for(j=0; j<LEN; j++){
+ double sum=0;
+ ph=0;
+ for(k=0; k<tap_count; k++)
+ sum += filter[ph * tap_count + k] * sine[k+j];
+ filtered[j]= sum / (1<<FILTER_SHIFT);
+ ss+= sine[j + center] * sine[j + center];
+ ff+= filtered[j] * filtered[j];
+ sf+= sine[j + center] * filtered[j];
+ }
+ ss= sqrt(2*ss/LEN);
+ ff= sqrt(2*ff/LEN);
+ sf= 2*sf/LEN;
+ maxff= FFMAX(maxff, ff);
+ minff= FFMIN(minff, ff);
+ maxsf= FFMAX(maxsf, sf);
+ minsf= FFMIN(minsf, sf);
+ if(i%11==0){
+ av_log(NULL, AV_LOG_ERROR, "i:%4d ss:%f ff:%13.6e-%13.6e sf:%13.6e-%13.6e\n", i, ss, maxff, minff, maxsf, minsf);
+ minff=minsf= 2;
+ maxff=maxsf= -2;
+ }
+ }
+ }
+#endif
+
+ av_free(tab);
+ return 0;
+}
+
+ResampleContext *swri_resample_init(ResampleContext *c, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff){
+ double factor= FFMIN(out_rate * cutoff / in_rate, 1.0);
+ int phase_count= 1<<phase_shift;
+
+ if (!c || c->phase_shift != phase_shift || c->linear!=linear || c->factor != factor
+ || c->filter_length != FFMAX((int)ceil(filter_size/factor), 1)) {
+ c = av_mallocz(sizeof(*c));
+ if (!c)
+ return NULL;
+
+ c->phase_shift = phase_shift;
+ c->phase_mask = phase_count - 1;
+ c->linear = linear;
+ c->factor = factor;
+ c->filter_length = FFMAX((int)ceil(filter_size/factor), 1);
+ c->filter_bank = av_mallocz(c->filter_length*(phase_count+1)*sizeof(FELEM));
+ if (!c->filter_bank)
+ goto error;
+ if (build_filter(c->filter_bank, factor, c->filter_length, phase_count, 1<<FILTER_SHIFT, WINDOW_TYPE))
+ goto error;
+ memcpy(&c->filter_bank[c->filter_length*phase_count+1], c->filter_bank, (c->filter_length-1)*sizeof(FELEM));
+ c->filter_bank[c->filter_length*phase_count]= c->filter_bank[c->filter_length - 1];
+ }
+
+ c->compensation_distance= 0;
+ if(!av_reduce(&c->src_incr, &c->dst_incr, out_rate, in_rate * (int64_t)phase_count, INT32_MAX/2))
+ goto error;
+ c->ideal_dst_incr= c->dst_incr;
+
+ c->index= -phase_count*((c->filter_length-1)/2);
+ c->frac= 0;
+
+ return c;
+error:
+ av_free(c->filter_bank);
+ av_free(c);
+ return NULL;
+}
+
+void swri_resample_free(ResampleContext **c){
+ if(!*c)
+ return;
+ av_freep(&(*c)->filter_bank);
+ av_freep(c);
+}
+
+void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance){
+ ResampleContext *c= s->resample;
+// sample_delta += (c->ideal_dst_incr - c->dst_incr)*(int64_t)c->compensation_distance / c->ideal_dst_incr;
+ c->compensation_distance= compensation_distance;
+ c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance;
+}
+
+int swri_resample(ResampleContext *c, int16_t *dst, const int16_t *src, int *consumed, int src_size, int dst_size, int update_ctx){
+ int dst_index, i;
+ int index= c->index;
+ int frac= c->frac;
+ int dst_incr_frac= c->dst_incr % c->src_incr;
+ int dst_incr= c->dst_incr / c->src_incr;
+ int compensation_distance= c->compensation_distance;
+
+ if(compensation_distance == 0 && c->filter_length == 1 && c->phase_shift==0){
+ int64_t index2= ((int64_t)index)<<32;
+ int64_t incr= (1LL<<32) * c->dst_incr / c->src_incr;
+ dst_size= FFMIN(dst_size, (src_size-1-index) * (int64_t)c->src_incr / c->dst_incr);
+
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ dst[dst_index] = src[index2>>32];
+ index2 += incr;
+ }
+ index += dst_index * dst_incr;
+ index += (frac + dst_index * (int64_t)dst_incr_frac) / c->src_incr;
+ frac = (frac + dst_index * (int64_t)dst_incr_frac) % c->src_incr;
+ }else{
+ for(dst_index=0; dst_index < dst_size; dst_index++){
+ FELEM *filter= c->filter_bank + c->filter_length*(index & c->phase_mask);
+ int sample_index= index >> c->phase_shift;
+ FELEM2 val=0;
+
+ if(sample_index < 0){
+ for(i=0; i<c->filter_length; i++)
+ val += src[FFABS(sample_index + i) % src_size] * filter[i];
+ }else if(sample_index + c->filter_length > src_size){
+ break;
+ }else if(c->linear){
+ FELEM2 v2=0;
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ v2 += src[sample_index + i] * (FELEM2)filter[i + c->filter_length];
+ }
+ val+=(v2-val)*(FELEML)frac / c->src_incr;
+ }else{
+ for(i=0; i<c->filter_length; i++){
+ val += src[sample_index + i] * (FELEM2)filter[i];
+ }
+ }
+
+#ifdef CONFIG_RESAMPLE_AUDIOPHILE_KIDDY_MODE
+ dst[dst_index] = av_clip_int16(lrintf(val));
+#else
+ val = (val + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;
+ dst[dst_index] = (unsigned)(val + 32768) > 65535 ? (val>>31) ^ 32767 : val;
+#endif
+
+ frac += dst_incr_frac;
+ index += dst_incr;
+ if(frac >= c->src_incr){
+ frac -= c->src_incr;
+ index++;
+ }
+
+ if(dst_index + 1 == compensation_distance){
+ compensation_distance= 0;
+ dst_incr_frac= c->ideal_dst_incr % c->src_incr;
+ dst_incr= c->ideal_dst_incr / c->src_incr;
+ }
+ }
+ }
+ *consumed= FFMAX(index, 0) >> c->phase_shift;
+ if(index>=0) index &= c->phase_mask;
+
+ if(compensation_distance){
+ compensation_distance -= dst_index;
+ assert(compensation_distance > 0);
+ }
+ if(update_ctx){
+ c->frac= frac;
+ c->index= index;
+ c->dst_incr= dst_incr_frac + c->src_incr*dst_incr;
+ c->compensation_distance= compensation_distance;
+ }
+#if 0
+ if(update_ctx && !c->compensation_distance){
+#undef rand
+ av_resample_compensate(c, rand() % (8000*2) - 8000, 8000*2);
+av_log(NULL, AV_LOG_DEBUG, "%d %d %d\n", c->dst_incr, c->ideal_dst_incr, c->compensation_distance);
+ }
+#endif
+
+ return dst_index;
+}
+
+int swri_multiple_resample(ResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed){
+ int i, ret= -1;
+
+ for(i=0; i<dst->ch_count; i++){
+ ret= swri_resample(c, (int16_t*)dst->ch[i], (const int16_t*)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count);
+ }
+
+ return ret;
+}
diff --git a/libswresample/swresample.c b/libswresample/swresample.c
new file mode 100644
index 0000000000..0575f7dead
--- /dev/null
+++ b/libswresample/swresample.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample 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.
+ *
+ * libswresample 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 libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/opt.h"
+#include "swresample_internal.h"
+#include "audioconvert.h"
+#include "libavutil/avassert.h"
+#include "libavutil/audioconvert.h"
+
+#define C30DB M_SQRT2
+#define C15DB 1.189207115
+#define C__0DB 1.0
+#define C_15DB 0.840896415
+#define C_30DB M_SQRT1_2
+#define C_45DB 0.594603558
+#define C_60DB 0.5
+
+
+//TODO split options array out?
+#define OFFSET(x) offsetof(SwrContext,x)
+static const AVOption options[]={
+{"ich", "input channel count", OFFSET( in.ch_count ), AV_OPT_TYPE_INT, {.dbl=2}, 0, SWR_CH_MAX, 0},
+{"och", "output channel count", OFFSET(out.ch_count ), AV_OPT_TYPE_INT, {.dbl=2}, 0, SWR_CH_MAX, 0},
+{"uch", "used channel count", OFFSET(used_ch_count ), AV_OPT_TYPE_INT, {.dbl=0}, 0, SWR_CH_MAX, 0},
+{"isr", "input sample rate" , OFFSET( in_sample_rate), AV_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0},
+{"osr", "output sample rate" , OFFSET(out_sample_rate), AV_OPT_TYPE_INT, {.dbl=48000}, 1, INT_MAX, 0},
+//{"ip" , "input planar" , OFFSET( in.planar ), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0},
+//{"op" , "output planar" , OFFSET(out.planar ), AV_OPT_TYPE_INT, {.dbl=0}, 0, 1, 0},
+{"isf", "input sample format", OFFSET( in_sample_fmt ), AV_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1+256, 0},
+{"osf", "output sample format", OFFSET(out_sample_fmt ), AV_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_S16}, 0, AV_SAMPLE_FMT_NB-1+256, 0},
+{"tsf", "internal sample format", OFFSET(int_sample_fmt ), AV_OPT_TYPE_INT, {.dbl=AV_SAMPLE_FMT_NONE}, -1, AV_SAMPLE_FMT_FLT, 0},
+{"icl", "input channel layout" , OFFSET( in_ch_layout), AV_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"},
+{"ocl", "output channel layout", OFFSET(out_ch_layout), AV_OPT_TYPE_INT64, {.dbl=0}, 0, INT64_MAX, 0, "channel_layout"},
+{"clev", "center mix level" , OFFSET(clev) , AV_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0},
+{"slev", "sourround mix level" , OFFSET(slev) , AV_OPT_TYPE_FLOAT, {.dbl=C_30DB}, 0, 4, 0},
+{"rmvol", "rematrix volume" , OFFSET(rematrix_volume), AV_OPT_TYPE_FLOAT, {.dbl=1.0}, -1000, 1000, 0},
+{"flags", NULL , OFFSET(flags) , AV_OPT_TYPE_FLAGS, {.dbl=0}, 0, UINT_MAX, 0, "flags"},
+{"res", "force resampling", 0, AV_OPT_TYPE_CONST, {.dbl=SWR_FLAG_RESAMPLE}, INT_MIN, INT_MAX, 0, "flags"},
+
+{0}
+};
+
+static const char* context_to_name(void* ptr) {
+ return "SWR";
+}
+
+static const AVClass av_class = {
+ .class_name = "SwrContext",
+ .item_name = context_to_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .log_level_offset_offset = OFFSET(log_level_offset),
+ .parent_log_context_offset = OFFSET(log_ctx),
+};
+
+int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map){
+ if(!s || s->in_convert) // s needs to be allocated but not initialized
+ return AVERROR(EINVAL);
+ s->channel_map = channel_map;
+ return 0;
+}
+
+struct SwrContext *swr_alloc(void){
+ SwrContext *s= av_mallocz(sizeof(SwrContext));
+ if(s){
+ s->av_class= &av_class;
+ av_opt_set_defaults(s);
+ }
+ return s;
+}
+
+struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
+ int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
+ int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
+ int log_offset, void *log_ctx){
+ if(!s) s= swr_alloc();
+ if(!s) return NULL;
+
+ s->log_level_offset= log_offset;
+ s->log_ctx= log_ctx;
+
+ av_opt_set_int(s, "ocl", out_ch_layout, 0);
+ av_opt_set_int(s, "osf", out_sample_fmt, 0);
+ av_opt_set_int(s, "osr", out_sample_rate, 0);
+ av_opt_set_int(s, "icl", in_ch_layout, 0);
+ av_opt_set_int(s, "isf", in_sample_fmt, 0);
+ av_opt_set_int(s, "isr", in_sample_rate, 0);
+ av_opt_set_int(s, "tsf", AV_SAMPLE_FMT_S16, 0);
+ av_opt_set_int(s, "ich", av_get_channel_layout_nb_channels(s-> in_ch_layout), 0);
+ av_opt_set_int(s, "och", av_get_channel_layout_nb_channels(s->out_ch_layout), 0);
+ av_opt_set_int(s, "uch", 0, 0);
+ return s;
+}
+
+
+static void free_temp(AudioData *a){
+ av_free(a->data);
+ memset(a, 0, sizeof(*a));
+}
+
+void swr_free(SwrContext **ss){
+ SwrContext *s= *ss;
+ if(s){
+ free_temp(&s->postin);
+ free_temp(&s->midbuf);
+ free_temp(&s->preout);
+ free_temp(&s->in_buffer);
+ swri_audio_convert_free(&s-> in_convert);
+ swri_audio_convert_free(&s->out_convert);
+ swri_audio_convert_free(&s->full_convert);
+ swri_resample_free(&s->resample);
+ }
+
+ av_freep(ss);
+}
+
+int swr_init(struct SwrContext *s){
+ s->in_buffer_index= 0;
+ s->in_buffer_count= 0;
+ s->resample_in_constraint= 0;
+ free_temp(&s->postin);
+ free_temp(&s->midbuf);
+ free_temp(&s->preout);
+ free_temp(&s->in_buffer);
+ swri_audio_convert_free(&s-> in_convert);
+ swri_audio_convert_free(&s->out_convert);
+ swri_audio_convert_free(&s->full_convert);
+
+ s-> in.planar= av_sample_fmt_is_planar(s-> in_sample_fmt);
+ s->out.planar= av_sample_fmt_is_planar(s->out_sample_fmt);
+ s-> in_sample_fmt= av_get_alt_sample_fmt(s-> in_sample_fmt, 0);
+ s->out_sample_fmt= av_get_alt_sample_fmt(s->out_sample_fmt, 0);
+
+ if(s-> in_sample_fmt >= AV_SAMPLE_FMT_NB){
+ av_log(s, AV_LOG_ERROR, "Requested input sample format %d is invalid\n", s->in_sample_fmt);
+ return AVERROR(EINVAL);
+ }
+ if(s->out_sample_fmt >= AV_SAMPLE_FMT_NB){
+ av_log(s, AV_LOG_ERROR, "Requested output sample format %d is invalid\n", s->out_sample_fmt);
+ return AVERROR(EINVAL);
+ }
+
+ if( s->int_sample_fmt != AV_SAMPLE_FMT_S16
+ &&s->int_sample_fmt != AV_SAMPLE_FMT_FLT){
+ av_log(s, AV_LOG_ERROR, "Requested sample format %s is not supported internally, only float & S16 is supported\n", av_get_sample_fmt_name(s->int_sample_fmt));
+ return AVERROR(EINVAL);
+ }
+
+ //FIXME should we allow/support using FLT on material that doesnt need it ?
+ if(s->in_sample_fmt <= AV_SAMPLE_FMT_S16 || s->int_sample_fmt==AV_SAMPLE_FMT_S16){
+ s->int_sample_fmt= AV_SAMPLE_FMT_S16;
+ }else
+ s->int_sample_fmt= AV_SAMPLE_FMT_FLT;
+
+
+ if (s->out_sample_rate!=s->in_sample_rate || (s->flags & SWR_FLAG_RESAMPLE)){
+ s->resample = swri_resample_init(s->resample, s->out_sample_rate, s->in_sample_rate, 16, 10, 0, 0.8);
+ }else
+ swri_resample_free(&s->resample);
+ if(s->int_sample_fmt != AV_SAMPLE_FMT_S16 && s->resample){
+ av_log(s, AV_LOG_ERROR, "Resampling only supported with internal s16 currently\n"); //FIXME
+ return -1;
+ }
+
+ if(!s->used_ch_count)
+ s->used_ch_count= s->in.ch_count;
+
+ if(s->used_ch_count && s-> in_ch_layout && s->used_ch_count != av_get_channel_layout_nb_channels(s-> in_ch_layout)){
+ av_log(s, AV_LOG_WARNING, "Input channel layout has a different number of channels than the number of used channels, ignoring layout\n");
+ s-> in_ch_layout= 0;
+ }
+
+ if(!s-> in_ch_layout)
+ s-> in_ch_layout= av_get_default_channel_layout(s->used_ch_count);
+ if(!s->out_ch_layout)
+ s->out_ch_layout= av_get_default_channel_layout(s->out.ch_count);
+
+ s->rematrix= s->out_ch_layout !=s->in_ch_layout || s->rematrix_volume!=1.0;
+
+#define RSC 1 //FIXME finetune
+ if(!s-> in.ch_count)
+ s-> in.ch_count= av_get_channel_layout_nb_channels(s-> in_ch_layout);
+ if(!s->used_ch_count)
+ s->used_ch_count= s->in.ch_count;
+ if(!s->out.ch_count)
+ s->out.ch_count= av_get_channel_layout_nb_channels(s->out_ch_layout);
+
+av_assert0(s-> in.ch_count);
+av_assert0(s->used_ch_count);
+av_assert0(s->out.ch_count);
+ s->resample_first= RSC*s->out.ch_count/s->in.ch_count - RSC < s->out_sample_rate/(float)s-> in_sample_rate - 1.0;
+
+ s-> in.bps= av_get_bytes_per_sample(s-> in_sample_fmt);
+ s->int_bps= av_get_bytes_per_sample(s->int_sample_fmt);
+ s->out.bps= av_get_bytes_per_sample(s->out_sample_fmt);
+
+ if(!s->resample && !s->rematrix && !s->channel_map){
+ s->full_convert = swri_audio_convert_alloc(s->out_sample_fmt,
+ s-> in_sample_fmt, s-> in.ch_count, NULL, 0);
+ return 0;
+ }
+
+ s->in_convert = swri_audio_convert_alloc(s->int_sample_fmt,
+ s-> in_sample_fmt, s->used_ch_count, s->channel_map, 0);
+ s->out_convert= swri_audio_convert_alloc(s->out_sample_fmt,
+ s->int_sample_fmt, s->out.ch_count, NULL, 0);
+
+
+ s->postin= s->in;
+ s->preout= s->out;
+ s->midbuf= s->in;
+ s->in_buffer= s->in;
+ if(s->channel_map){
+ s->postin.ch_count=
+ s->midbuf.ch_count=
+ s->in_buffer.ch_count= s->used_ch_count;
+ }
+ if(!s->resample_first){
+ s->midbuf.ch_count= s->out.ch_count;
+ s->in_buffer.ch_count = s->out.ch_count;
+ }
+
+ s->in_buffer.bps = s->postin.bps = s->midbuf.bps = s->preout.bps = s->int_bps;
+ s->in_buffer.planar = s->postin.planar = s->midbuf.planar = s->preout.planar = 1;
+
+
+ if(s->rematrix)
+ return swri_rematrix_init(s);
+
+ return 0;
+}
+
+static int realloc_audio(AudioData *a, int count){
+ int i, countb;
+ AudioData old;
+
+ if(a->count >= count)
+ return 0;
+
+ count*=2;
+
+ countb= FFALIGN(count*a->bps, 32);
+ old= *a;
+
+ av_assert0(a->planar);
+ av_assert0(a->bps);
+ av_assert0(a->ch_count);
+
+ a->data= av_malloc(countb*a->ch_count);
+ if(!a->data)
+ return AVERROR(ENOMEM);
+ for(i=0; i<a->ch_count; i++){
+ a->ch[i]= a->data + i*(a->planar ? countb : a->bps);
+ if(a->planar) memcpy(a->ch[i], old.ch[i], a->count*a->bps);
+ }
+ av_free(old.data);
+ a->count= count;
+
+ return 1;
+}
+
+static void copy(AudioData *out, AudioData *in,
+ int count){
+ av_assert0(out->planar == in->planar);
+ av_assert0(out->bps == in->bps);
+ av_assert0(out->ch_count == in->ch_count);
+ if(out->planar){
+ int ch;
+ for(ch=0; ch<out->ch_count; ch++)
+ memcpy(out->ch[ch], in->ch[ch], count*out->bps);
+ }else
+ memcpy(out->ch[0], in->ch[0], count*out->ch_count*out->bps);
+}
+
+static void fill_audiodata(AudioData *out, uint8_t *in_arg [SWR_CH_MAX]){
+ int i;
+ if(out->planar){
+ for(i=0; i<out->ch_count; i++)
+ out->ch[i]= in_arg[i];
+ }else{
+ for(i=0; i<out->ch_count; i++)
+ out->ch[i]= in_arg[0] + i*out->bps;
+ }
+}
+
+/**
+ *
+ * out may be equal in.
+ */
+static void buf_set(AudioData *out, AudioData *in, int count){
+ if(in->planar){
+ int ch;
+ for(ch=0; ch<out->ch_count; ch++)
+ out->ch[ch]= in->ch[ch] + count*out->bps;
+ }else
+ out->ch[0]= in->ch[0] + count*out->ch_count*out->bps;
+}
+
+/**
+ *
+ * @return number of samples output per channel
+ */
+static int resample(SwrContext *s, AudioData *out_param, int out_count,
+ const AudioData * in_param, int in_count){
+ AudioData in, out, tmp;
+ int ret_sum=0;
+ int border=0;
+
+ tmp=out=*out_param;
+ in = *in_param;
+
+ do{
+ int ret, size, consumed;
+ if(!s->resample_in_constraint && s->in_buffer_count){
+ buf_set(&tmp, &s->in_buffer, s->in_buffer_index);
+ ret= swri_multiple_resample(s->resample, &out, out_count, &tmp, s->in_buffer_count, &consumed);
+ out_count -= ret;
+ ret_sum += ret;
+ buf_set(&out, &out, ret);
+ s->in_buffer_count -= consumed;
+ s->in_buffer_index += consumed;
+
+ if(!in_count)
+ break;
+ if(s->in_buffer_count <= border){
+ buf_set(&in, &in, -s->in_buffer_count);
+ in_count += s->in_buffer_count;
+ s->in_buffer_count=0;
+ s->in_buffer_index=0;
+ border = 0;
+ }
+ }
+
+ if(in_count && !s->in_buffer_count){
+ s->in_buffer_index=0;
+ ret= swri_multiple_resample(s->resample, &out, out_count, &in, in_count, &consumed);
+ out_count -= ret;
+ ret_sum += ret;
+ buf_set(&out, &out, ret);
+ in_count -= consumed;
+ buf_set(&in, &in, consumed);
+ }
+
+ //TODO is this check sane considering the advanced copy avoidance below
+ size= s->in_buffer_index + s->in_buffer_count + in_count;
+ if( size > s->in_buffer.count
+ && s->in_buffer_count + in_count <= s->in_buffer_index){
+ buf_set(&tmp, &s->in_buffer, s->in_buffer_index);
+ copy(&s->in_buffer, &tmp, s->in_buffer_count);
+ s->in_buffer_index=0;
+ }else
+ if((ret=realloc_audio(&s->in_buffer, size)) < 0)
+ return ret;
+
+ if(in_count){
+ int count= in_count;
+ if(s->in_buffer_count && s->in_buffer_count+2 < count && out_count) count= s->in_buffer_count+2;
+
+ buf_set(&tmp, &s->in_buffer, s->in_buffer_index + s->in_buffer_count);
+ copy(&tmp, &in, /*in_*/count);
+ s->in_buffer_count += count;
+ in_count -= count;
+ border += count;
+ buf_set(&in, &in, count);
+ s->resample_in_constraint= 0;
+ if(s->in_buffer_count != count || in_count)
+ continue;
+ }
+ break;
+ }while(1);
+
+ s->resample_in_constraint= !!out_count;
+
+ return ret_sum;
+}
+
+int swr_convert(struct SwrContext *s, uint8_t *out_arg[SWR_CH_MAX], int out_count,
+ const uint8_t *in_arg [SWR_CH_MAX], int in_count){
+ AudioData *postin, *midbuf, *preout;
+ int ret/*, in_max*/;
+ AudioData * in= &s->in;
+ AudioData *out= &s->out;
+ AudioData preout_tmp, midbuf_tmp;
+
+ if(!s->resample){
+ if(in_count > out_count)
+ return -1;
+ out_count = in_count;
+ }
+
+ if(!in_arg){
+ if(s->in_buffer_count){
+ AudioData *a= &s->in_buffer;
+ int i, j, ret;
+ if((ret=realloc_audio(a, s->in_buffer_index + 2*s->in_buffer_count)) < 0)
+ return ret;
+ av_assert0(a->planar);
+ for(i=0; i<a->ch_count; i++){
+ for(j=0; j<s->in_buffer_count; j++){
+ memcpy(a->ch[i] + (s->in_buffer_index+s->in_buffer_count+j )*a->bps,
+ a->ch[i] + (s->in_buffer_index+s->in_buffer_count-j-1)*a->bps, a->bps);
+ }
+ }
+ s->in_buffer_count += (s->in_buffer_count+1)/2;
+ s->resample_in_constraint = 0;
+ }else{
+ return 0;
+ }
+ }else
+ fill_audiodata(in , (void*)in_arg);
+ fill_audiodata(out, out_arg);
+
+ if(s->full_convert){
+ av_assert0(!s->resample);
+ swri_audio_convert(s->full_convert, out, in, in_count);
+ return out_count;
+ }
+
+// in_max= out_count*(int64_t)s->in_sample_rate / s->out_sample_rate + resample_filter_taps;
+// in_count= FFMIN(in_count, in_in + 2 - s->hist_buffer_count);
+
+ if((ret=realloc_audio(&s->postin, in_count))<0)
+ return ret;
+ if(s->resample_first){
+ av_assert0(s->midbuf.ch_count == s->used_ch_count);
+ if((ret=realloc_audio(&s->midbuf, out_count))<0)
+ return ret;
+ }else{
+ av_assert0(s->midbuf.ch_count == s->out.ch_count);
+ if((ret=realloc_audio(&s->midbuf, in_count))<0)
+ return ret;
+ }
+ if((ret=realloc_audio(&s->preout, out_count))<0)
+ return ret;
+
+ postin= &s->postin;
+
+ midbuf_tmp= s->midbuf;
+ midbuf= &midbuf_tmp;
+ preout_tmp= s->preout;
+ preout= &preout_tmp;
+
+ if(s->int_sample_fmt == s-> in_sample_fmt && s->in.planar)
+ postin= in;
+
+ if(s->resample_first ? !s->resample : !s->rematrix)
+ midbuf= postin;
+
+ if(s->resample_first ? !s->rematrix : !s->resample)
+ preout= midbuf;
+
+ if(s->int_sample_fmt == s->out_sample_fmt && s->out.planar){
+ if(preout==in){
+ out_count= FFMIN(out_count, in_count); //TODO check at teh end if this is needed or redundant
+ av_assert0(s->in.planar); //we only support planar internally so it has to be, we support copying non planar though
+ copy(out, in, out_count);
+ return out_count;
+ }
+ else if(preout==postin) preout= midbuf= postin= out;
+ else if(preout==midbuf) preout= midbuf= out;
+ else preout= out;
+ }
+
+ if(in != postin){
+ swri_audio_convert(s->in_convert, postin, in, in_count);
+ }
+
+ if(s->resample_first){
+ if(postin != midbuf)
+ out_count= resample(s, midbuf, out_count, postin, in_count);
+ if(midbuf != preout)
+ swri_rematrix(s, preout, midbuf, out_count, preout==out);
+ }else{
+ if(postin != midbuf)
+ swri_rematrix(s, midbuf, postin, in_count, midbuf==out);
+ if(midbuf != preout)
+ out_count= resample(s, preout, out_count, midbuf, in_count);
+ }
+
+ if(preout != out){
+//FIXME packed doesnt need more than 1 chan here!
+ swri_audio_convert(s->out_convert, out, preout, out_count);
+ }
+ if(!in_arg)
+ s->in_buffer_count = 0;
+ return out_count;
+}
+
diff --git a/libswresample/swresample.h b/libswresample/swresample.h
new file mode 100644
index 0000000000..fad09358d5
--- /dev/null
+++ b/libswresample/swresample.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample 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.
+ *
+ * libswresample 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 libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * libswresample public header
+ */
+
+#ifndef SWR_H
+#define SWR_H
+
+#include <inttypes.h>
+#include "libavutil/samplefmt.h"
+
+#define LIBSWRESAMPLE_VERSION_MAJOR 0
+#define LIBSWRESAMPLE_VERSION_MINOR 5
+#define LIBSWRESAMPLE_VERSION_MICRO 0
+
+#define SWR_CH_MAX 16 ///< Maximum number of channels
+
+#define SWR_FLAG_RESAMPLE 1 ///< Force resampling even if equal sample rate
+//TODO use int resample ?
+//long term TODO can we enable this dynamically?
+
+
+struct SwrContext;
+
+/**
+ * Allocate SwrContext.
+ *
+ * If you use this function you will need to set the parameters (manually or
+ * with swr_alloc_set_opts()) before calling swr_init().
+ *
+ * @see swr_alloc_set_opts(), swr_init(), swr_free()
+ * @return NULL on error, allocated context otherwise
+ */
+struct SwrContext *swr_alloc(void);
+
+/**
+ * Initialize context after user parameters have been set.
+ *
+ * @return AVERROR error code in case of failure.
+ */
+int swr_init(struct SwrContext *s);
+
+/**
+ * Allocate SwrContext if needed and set/reset common parameters.
+ *
+ * This function does not require s to be allocated with swr_alloc(). On the
+ * other hand, swr_alloc() can use swr_alloc_set_opts() to set the parameters
+ * on the allocated context.
+ *
+ * @param s Swr context, can be NULL
+ * @param out_ch_layout output channel layout (AV_CH_LAYOUT_*)
+ * @param out_sample_fmt output sample format (AV_SAMPLE_FMT_*).
+ * @param out_sample_rate output sample rate (frequency in Hz)
+ * @param in_ch_layout input channel layout (AV_CH_LAYOUT_*)
+ * @param in_sample_fmt input sample format (AV_SAMPLE_FMT_*).
+ * @param in_sample_rate input sample rate (frequency in Hz)
+ * @param log_offset logging level offset
+ * @param log_ctx parent logging context, can be NULL
+ *
+ * @see swr_init(), swr_free()
+ * @return NULL on error, allocated context otherwise
+ */
+struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
+ int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
+ int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
+ int log_offset, void *log_ctx);
+
+/**
+ * Free the given SwrContext and set the pointer to NULL.
+ */
+void swr_free(struct SwrContext **s);
+
+/**
+ * Convert audio.
+ *
+ * in and in_count can be set to 0 to flush the last few samples out at the
+ * end.
+ *
+ * @param s allocated Swr context, with parameters set
+ * @param out output buffers, only the first one need be set in case of packed audio
+ * @param out_count amount of space available for output in samples per channel
+ * @param in input buffers, only the first one need to be set in case of packed audio
+ * @param in_count number of input samples available in one channel
+ *
+ * @return number of samples output per channel
+ */
+int swr_convert(struct SwrContext *s, uint8_t *out[SWR_CH_MAX], int out_count,
+ const uint8_t *in [SWR_CH_MAX], int in_count);
+
+/**
+ * Activate resampling compensation.
+ */
+void swr_compensate(struct SwrContext *s, int sample_delta, int compensation_distance);
+
+/**
+ * Set a customized input channel mapping.
+ *
+ * @param s allocated Swr context, not yet initialized
+ * @param channel_map customized input channel mapping (array of channel
+ * indexes, -1 for a muted channel)
+ * @return AVERROR error code in case of failure.
+ */
+int swr_set_channel_mapping(struct SwrContext *s, const int *channel_map);
+
+#endif
diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h
new file mode 100644
index 0000000000..0fc1c6b6a6
--- /dev/null
+++ b/libswresample/swresample_internal.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample 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.
+ *
+ * libswresample 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 libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SWR_INTERNAL_H
+#define SWR_INTERNAL_H
+
+#include "swresample.h"
+
+typedef struct AudioData{
+ uint8_t *ch[SWR_CH_MAX]; ///< samples buffer per channel
+ uint8_t *data; ///< samples buffer
+ int ch_count; ///< number of channels
+ int bps; ///< bytes per sample
+ int count; ///< number of samples
+ int planar; ///< 1 if planar audio, 0 otherwise
+} AudioData;
+
+typedef struct SwrContext {
+ const AVClass *av_class; ///< AVClass used for AVOption and av_log()
+ int log_level_offset; ///< logging level offset
+ void *log_ctx; ///< parent logging context
+ enum AVSampleFormat in_sample_fmt; ///< input sample format
+ enum AVSampleFormat int_sample_fmt; ///< internal sample format (AV_SAMPLE_FMT_FLT or AV_SAMPLE_FMT_S16)
+ enum AVSampleFormat out_sample_fmt; ///< output sample format
+ int64_t in_ch_layout; ///< input channel layout
+ int64_t out_ch_layout; ///< output channel layout
+ int in_sample_rate; ///< input sample rate
+ int out_sample_rate; ///< output sample rate
+ int flags; ///< miscellaneous flags such as SWR_FLAG_RESAMPLE
+ float slev; ///< surround mixing level
+ float clev; ///< center mixing level
+ float rematrix_volume; ///< rematrixing volume coefficient
+ const int *channel_map; ///< channel index (or -1 if muted channel) map
+ int used_ch_count; ///< number of used input channels (mapped channel count if channel_map, otherwise in.ch_count)
+
+ int int_bps; ///< internal bytes per sample
+ int resample_first; ///< 1 if resampling must come first, 0 if rematrixing
+ int rematrix; ///< flag to indicate if rematrixing is needed (basically if input and output layouts mismatch)
+
+ AudioData in; ///< input audio data
+ AudioData postin; ///< post-input audio data: used for rematrix/resample
+ AudioData midbuf; ///< intermediate audio data (postin/preout)
+ AudioData preout; ///< pre-output audio data: used for rematrix/resample
+ AudioData out; ///< converted output audio data
+ AudioData in_buffer; ///< cached audio data (convert and resample purpose)
+ int in_buffer_index; ///< cached buffer position
+ int in_buffer_count; ///< cached buffer length
+ int resample_in_constraint; ///< 1 if the input end was reach before the output end, 0 otherwise
+
+ struct AudioConvert *in_convert; ///< input conversion context
+ struct AudioConvert *out_convert; ///< output conversion context
+ struct AudioConvert *full_convert; ///< full conversion context (single conversion for input and output)
+ struct ResampleContext *resample; ///< resampling context
+
+ float matrix[SWR_CH_MAX][SWR_CH_MAX]; ///< floating point rematrixing coefficients
+ int32_t matrix32[SWR_CH_MAX][SWR_CH_MAX]; ///< 17.15 fixed point rematrixing coefficients
+ uint8_t matrix_ch[SWR_CH_MAX][SWR_CH_MAX+1]; ///< Lists of input channels per output channel that have non zero rematrixing coefficients
+
+ /* TODO: callbacks for ASM optimizations */
+}SwrContext;
+
+struct ResampleContext *swri_resample_init(struct ResampleContext *, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff);
+void swri_resample_free(struct ResampleContext **c);
+int swri_multiple_resample(struct ResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed);
+void swri_resample_compensate(struct ResampleContext *c, int sample_delta, int compensation_distance);
+int swri_resample(struct ResampleContext *c, int16_t *dst, const int16_t *src, int *consumed, int src_size, int dst_size, int update_ctx);
+
+int swri_rematrix_init(SwrContext *s);
+int swri_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy);
+
+#endif
diff --git a/libswresample/swresample_test.c b/libswresample/swresample_test.c
new file mode 100644
index 0000000000..d41b964ba4
--- /dev/null
+++ b/libswresample/swresample_test.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at)
+ *
+ * This file is part of libswresample
+ *
+ * libswresample is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * libswresample 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libswresample; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/common.h"
+#include "libavutil/audioconvert.h"
+#include "swresample.h"
+#undef fprintf
+
+#define SAMPLES 1000
+
+#define ASSERT_LEVEL 2
+
+static double get(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f){
+ const uint8_t *p;
+ if(av_sample_fmt_is_planar(f)){
+ f= av_get_alt_sample_fmt(f, 0);
+ p= a[ch];
+ }else{
+ p= a[0];
+ index= ch + index*ch_count;
+ }
+
+ switch(f){
+ case AV_SAMPLE_FMT_U8 : return ((const uint8_t*)p)[index]/255.0*2-1.0;
+ case AV_SAMPLE_FMT_S16: return ((const int16_t*)p)[index]/32767.0;
+ case AV_SAMPLE_FMT_S32: return ((const int32_t*)p)[index]/2147483647.0;
+ case AV_SAMPLE_FMT_FLT: return ((const float *)p)[index];
+ case AV_SAMPLE_FMT_DBL: return ((const double *)p)[index];
+ default: av_assert0(0);
+ }
+}
+
+static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v){
+ uint8_t *p;
+ if(av_sample_fmt_is_planar(f)){
+ f= av_get_alt_sample_fmt(f, 0);
+ p= a[ch];
+ }else{
+ p= a[0];
+ index= ch + index*ch_count;
+ }
+ switch(f){
+ case AV_SAMPLE_FMT_U8 : ((uint8_t*)p)[index]= (v+1.0)*255.0/2; break;
+ case AV_SAMPLE_FMT_S16: ((int16_t*)p)[index]= v*32767; break;
+ case AV_SAMPLE_FMT_S32: ((int32_t*)p)[index]= v*2147483647; break;
+ case AV_SAMPLE_FMT_FLT: ((float *)p)[index]= v; break;
+ case AV_SAMPLE_FMT_DBL: ((double *)p)[index]= v; break;
+ default: av_assert2(0);
+ }
+}
+
+uint64_t layouts[]={
+AV_CH_LAYOUT_MONO ,
+AV_CH_LAYOUT_STEREO ,
+AV_CH_LAYOUT_2_1 ,
+AV_CH_LAYOUT_SURROUND ,
+AV_CH_LAYOUT_4POINT0 ,
+AV_CH_LAYOUT_2_2 ,
+AV_CH_LAYOUT_QUAD ,
+AV_CH_LAYOUT_5POINT0 ,
+AV_CH_LAYOUT_5POINT1 ,
+AV_CH_LAYOUT_5POINT0_BACK ,
+AV_CH_LAYOUT_5POINT1_BACK ,
+AV_CH_LAYOUT_7POINT0 ,
+AV_CH_LAYOUT_7POINT1 ,
+AV_CH_LAYOUT_7POINT1_WIDE ,
+0
+};
+
+static void setup_array(uint8_t *out[SWR_CH_MAX], uint8_t *in, enum AVSampleFormat format, int samples){
+ if(av_sample_fmt_is_planar(format)){
+ int i;
+ int plane_size= av_get_bytes_per_sample(format&0xFF)*samples;
+ format&=0xFF;
+ for(i=0; i<SWR_CH_MAX; i++){
+ out[i]= in + i*plane_size;
+ }
+ }else{
+ out[0]= in;
+ }
+}
+
+int main(int argc, char **argv){
+ int in_sample_rate, out_sample_rate, ch ,i, in_ch_layout_index, out_ch_layout_index, osr, flush_count;
+ uint64_t in_ch_layout, out_ch_layout;
+ enum AVSampleFormat in_sample_fmt, out_sample_fmt;
+ int sample_rates[]={8000,11025,16000,22050,32000};
+ uint8_t array_in[SAMPLES*8*8];
+ uint8_t array_mid[SAMPLES*8*8*3];
+ uint8_t array_out[SAMPLES*8*8+100];
+ uint8_t *ain[SWR_CH_MAX];
+ uint8_t *aout[SWR_CH_MAX];
+ uint8_t *amid[SWR_CH_MAX];
+
+ struct SwrContext * forw_ctx= NULL;
+ struct SwrContext *backw_ctx= NULL;
+
+ in_sample_rate=16000;
+ for(osr=0; osr<5; osr++){
+ out_sample_rate= sample_rates[osr];
+ for(in_sample_fmt= AV_SAMPLE_FMT_U8; in_sample_fmt<=AV_SAMPLE_FMT_DBL; in_sample_fmt++){
+ for(out_sample_fmt= AV_SAMPLE_FMT_U8; out_sample_fmt<=AV_SAMPLE_FMT_DBL; out_sample_fmt++){
+ for(in_ch_layout_index=0; layouts[in_ch_layout_index]; in_ch_layout_index++){
+ int in_ch_count;
+ in_ch_layout= layouts[in_ch_layout_index];
+ in_ch_count= av_get_channel_layout_nb_channels(in_ch_layout);
+ for(out_ch_layout_index=0; layouts[out_ch_layout_index]; out_ch_layout_index++){
+ int out_count, mid_count, out_ch_count;
+ out_ch_layout= layouts[out_ch_layout_index];
+ out_ch_count= av_get_channel_layout_nb_channels(out_ch_layout);
+ fprintf(stderr, "ch %d->%d, rate:%5d->%5d, fmt:%s->%s",
+ in_ch_count, out_ch_count,
+ in_sample_rate, out_sample_rate,
+ av_get_sample_fmt_name(in_sample_fmt), av_get_sample_fmt_name(out_sample_fmt));
+ forw_ctx = swr_alloc_set_opts(forw_ctx, out_ch_layout, av_get_alt_sample_fmt(out_sample_fmt, 1), out_sample_rate,
+ in_ch_layout, av_get_alt_sample_fmt( in_sample_fmt, 1), in_sample_rate,
+ 0, 0);
+ backw_ctx = swr_alloc_set_opts(backw_ctx, in_ch_layout, in_sample_fmt, in_sample_rate,
+ out_ch_layout, av_get_alt_sample_fmt(out_sample_fmt, 1), out_sample_rate,
+ 0, 0);
+ if(swr_init( forw_ctx) < 0)
+ fprintf(stderr, "swr_init(->) failed\n");
+ if(swr_init(backw_ctx) < 0)
+ fprintf(stderr, "swr_init(<-) failed\n");
+ if(!forw_ctx)
+ fprintf(stderr, "Failed to init forw_cts\n");
+ if(!backw_ctx)
+ fprintf(stderr, "Failed to init backw_ctx\n");
+ //FIXME test planar
+ setup_array(ain , array_in , av_get_alt_sample_fmt( in_sample_fmt, 1), SAMPLES);
+ setup_array(amid, array_mid, av_get_alt_sample_fmt(out_sample_fmt, 1), 3*SAMPLES);
+ setup_array(aout, array_out, in_sample_fmt , SAMPLES);
+ for(ch=0; ch<in_ch_count; ch++){
+ for(i=0; i<SAMPLES; i++)
+ set(ain, ch, i, in_ch_count, av_get_alt_sample_fmt(in_sample_fmt, 1), sin(i*i*3/SAMPLES));
+ }
+ mid_count= swr_convert(forw_ctx, amid, 3*SAMPLES, ain, SAMPLES);
+ out_count= swr_convert(backw_ctx,aout, SAMPLES, amid, mid_count);
+
+ for(ch=0; ch<in_ch_count; ch++){
+ double sse, x, maxdiff=0;
+ double sum_a= 0;
+ double sum_b= 0;
+ double sum_aa= 0;
+ double sum_bb= 0;
+ double sum_ab= 0;
+ for(i=0; i<out_count; i++){
+ double a= get(ain , ch, i, in_ch_count, av_get_alt_sample_fmt(in_sample_fmt, 1));
+ double b= get(aout, ch, i, in_ch_count, in_sample_fmt);
+ sum_a += a;
+ sum_b += b;
+ sum_aa+= a*a;
+ sum_bb+= b*b;
+ sum_ab+= a*b;
+ maxdiff= FFMAX(maxdiff, FFABS(a-b));
+ }
+ x = sum_ab/sum_bb;
+ sse= sum_aa + sum_bb*x*x - 2*x*sum_ab;
+
+ fprintf(stderr, "[%f %f %f] len:%5d\n", sqrt(sse/out_count), x, maxdiff, out_count);
+ }
+
+ flush_count=swr_convert(backw_ctx,aout, SAMPLES, 0, 0);
+ if(flush_count){
+ for(ch=0; ch<in_ch_count; ch++){
+ double sse, x, maxdiff=0;
+ double sum_a= 0;
+ double sum_b= 0;
+ double sum_aa= 0;
+ double sum_bb= 0;
+ double sum_ab= 0;
+ for(i=0; i<flush_count; i++){
+ double a= get(ain , ch, i+out_count, in_ch_count, av_get_alt_sample_fmt(in_sample_fmt, 1));
+ double b= get(aout, ch, i, in_ch_count, in_sample_fmt);
+ sum_a += a;
+ sum_b += b;
+ sum_aa+= a*a;
+ sum_bb+= b*b;
+ sum_ab+= a*b;
+ maxdiff= FFMAX(maxdiff, FFABS(a-b));
+ }
+ x = sum_ab/sum_bb;
+ sse= sum_aa + sum_bb*x*x - 2*x*sum_ab;
+
+ fprintf(stderr, "[%f %f %f] len:%5d\n", sqrt(sse/flush_count), x, maxdiff, flush_count);
+ }
+ }
+
+
+ fprintf(stderr, "\n");
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}