diff options
Diffstat (limited to 'libswresample')
-rw-r--r-- | libswresample/Makefile | 15 | ||||
-rw-r--r-- | libswresample/arm/Makefile | 2 | ||||
-rw-r--r-- | libswresample/arm/audio_convert_init.c | 65 | ||||
-rw-r--r-- | libswresample/arm/audio_convert_neon.S | 363 | ||||
-rw-r--r-- | libswresample/audioconvert.c | 208 | ||||
-rw-r--r-- | libswresample/audioconvert.h | 77 | ||||
-rw-r--r-- | libswresample/dither.c | 87 | ||||
-rw-r--r-- | libswresample/libswresample.v | 4 | ||||
-rw-r--r-- | libswresample/rematrix.c | 497 | ||||
-rw-r--r-- | libswresample/rematrix_template.c | 74 | ||||
-rw-r--r-- | libswresample/resample.c | 459 | ||||
-rw-r--r-- | libswresample/resample_template.c | 139 | ||||
-rw-r--r-- | libswresample/swresample-test.c | 410 | ||||
-rw-r--r-- | libswresample/swresample.c | 795 | ||||
-rw-r--r-- | libswresample/swresample.h | 220 | ||||
-rw-r--r-- | libswresample/swresample_internal.h | 142 | ||||
-rw-r--r-- | libswresample/version.h | 45 | ||||
-rw-r--r-- | libswresample/x86/Makefile | 3 | ||||
-rw-r--r-- | libswresample/x86/audio_convert.asm | 462 | ||||
-rw-r--r-- | libswresample/x86/rematrix.asm | 252 | ||||
-rw-r--r-- | libswresample/x86/resample_mmx.h | 70 | ||||
-rw-r--r-- | libswresample/x86/swresample_x86.c | 195 |
22 files changed, 4584 insertions, 0 deletions
diff --git a/libswresample/Makefile b/libswresample/Makefile new file mode 100644 index 0000000000..d85ed8be88 --- /dev/null +++ b/libswresample/Makefile @@ -0,0 +1,15 @@ +include $(SUBDIR)../config.mak + +NAME = swresample +FFLIBS = avutil + +HEADERS = swresample.h \ + version.h \ + +OBJS = audioconvert.o \ + dither.o \ + rematrix.o \ + resample.o \ + swresample.o \ + +TESTPROGS = swresample diff --git a/libswresample/arm/Makefile b/libswresample/arm/Makefile new file mode 100644 index 0000000000..55683cbfae --- /dev/null +++ b/libswresample/arm/Makefile @@ -0,0 +1,2 @@ +OBJS += arm/audio_convert_init.o +NEON-OBJS += arm/audio_convert_neon.o diff --git a/libswresample/arm/audio_convert_init.c b/libswresample/arm/audio_convert_init.c new file mode 100644 index 0000000000..9fdb174608 --- /dev/null +++ b/libswresample/arm/audio_convert_init.c @@ -0,0 +1,65 @@ +/* + * 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 <stdint.h> + +#include "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/arm/cpu.h" +#include "libavutil/samplefmt.h" +#include "libswresample/swresample_internal.h" +#include "libswresample/audioconvert.h" + +void swri_oldapi_conv_flt_to_s16_neon(int16_t *dst, const float *src, int len); +void swri_oldapi_conv_fltp_to_s16_2ch_neon(int16_t *dst, float *const *src, int len, int channels); +void swri_oldapi_conv_fltp_to_s16_nch_neon(int16_t *dst, float *const *src, int len, int channels); + +static void conv_flt_to_s16_neon(uint8_t **dst, const uint8_t **src, int len){ + swri_oldapi_conv_flt_to_s16_neon((int16_t*)*dst, (const float*)*src, len); +} + +static void conv_fltp_to_s16_2ch_neon(uint8_t **dst, const uint8_t **src, int len){ + swri_oldapi_conv_fltp_to_s16_2ch_neon((int16_t*)*dst, (float *const*)src, len, 2); +} + +static void conv_fltp_to_s16_nch_neon(uint8_t **dst, const uint8_t **src, int len){ + int channels; + for(channels=3; channels<SWR_CH_MAX && src[channels]; channels++) + ; + swri_oldapi_conv_fltp_to_s16_nch_neon((int16_t*)*dst, (float *const*)src, len, channels); +} + +av_cold void swri_audio_convert_init_arm(struct AudioConvert *ac, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels) +{ + int cpu_flags = av_get_cpu_flags(); + + ac->simd_f= NULL; + + if (have_neon(cpu_flags)) { + if(out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_FLT || out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = conv_flt_to_s16_neon; + if(out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_FLTP && channels == 2) + ac->simd_f = conv_fltp_to_s16_2ch_neon; + if(out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_FLTP && channels > 2) + ac->simd_f = conv_fltp_to_s16_nch_neon; + } +} diff --git a/libswresample/arm/audio_convert_neon.S b/libswresample/arm/audio_convert_neon.S new file mode 100644 index 0000000000..471a2d864f --- /dev/null +++ b/libswresample/arm/audio_convert_neon.S @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2008 Mans Rullgard <mans@mansr.com> + * + * 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 "config.h" +#include "libavutil/arm/asm.S" + +function swri_oldapi_conv_flt_to_s16_neon, export=1 + subs r2, r2, #8 + vld1.32 {q0}, [r1,:128]! + vcvt.s32.f32 q8, q0, #31 + vld1.32 {q1}, [r1,:128]! + vcvt.s32.f32 q9, q1, #31 + beq 3f + bics r12, r2, #15 + beq 2f +1: subs r12, r12, #16 + vqrshrn.s32 d4, q8, #16 + vld1.32 {q0}, [r1,:128]! + vcvt.s32.f32 q0, q0, #31 + vqrshrn.s32 d5, q9, #16 + vld1.32 {q1}, [r1,:128]! + vcvt.s32.f32 q1, q1, #31 + vqrshrn.s32 d6, q0, #16 + vst1.16 {q2}, [r0,:128]! + vqrshrn.s32 d7, q1, #16 + vld1.32 {q8}, [r1,:128]! + vcvt.s32.f32 q8, q8, #31 + vld1.32 {q9}, [r1,:128]! + vcvt.s32.f32 q9, q9, #31 + vst1.16 {q3}, [r0,:128]! + bne 1b + ands r2, r2, #15 + beq 3f +2: vld1.32 {q0}, [r1,:128]! + vqrshrn.s32 d4, q8, #16 + vcvt.s32.f32 q0, q0, #31 + vld1.32 {q1}, [r1,:128]! + vqrshrn.s32 d5, q9, #16 + vcvt.s32.f32 q1, q1, #31 + vqrshrn.s32 d6, q0, #16 + vst1.16 {q2}, [r0,:128]! + vqrshrn.s32 d7, q1, #16 + vst1.16 {q3}, [r0,:128]! + bx lr +3: vqrshrn.s32 d4, q8, #16 + vqrshrn.s32 d5, q9, #16 + vst1.16 {q2}, [r0,:128]! + bx lr +endfunc + +function swri_oldapi_conv_fltp_to_s16_2ch_neon, export=1 + ldm r1, {r1, r3} + subs r2, r2, #8 + vld1.32 {q0}, [r1,:128]! + vcvt.s32.f32 q8, q0, #31 + vld1.32 {q1}, [r1,:128]! + vcvt.s32.f32 q9, q1, #31 + vld1.32 {q10}, [r3,:128]! + vcvt.s32.f32 q10, q10, #31 + vld1.32 {q11}, [r3,:128]! + vcvt.s32.f32 q11, q11, #31 + beq 3f + bics r12, r2, #15 + beq 2f +1: subs r12, r12, #16 + vld1.32 {q0}, [r1,:128]! + vcvt.s32.f32 q0, q0, #31 + vsri.32 q10, q8, #16 + vld1.32 {q1}, [r1,:128]! + vcvt.s32.f32 q1, q1, #31 + vld1.32 {q12}, [r3,:128]! + vcvt.s32.f32 q12, q12, #31 + vld1.32 {q13}, [r3,:128]! + vsri.32 q11, q9, #16 + vst1.16 {q10}, [r0,:128]! + vcvt.s32.f32 q13, q13, #31 + vst1.16 {q11}, [r0,:128]! + vsri.32 q12, q0, #16 + vld1.32 {q8}, [r1,:128]! + vsri.32 q13, q1, #16 + vst1.16 {q12}, [r0,:128]! + vcvt.s32.f32 q8, q8, #31 + vld1.32 {q9}, [r1,:128]! + vcvt.s32.f32 q9, q9, #31 + vld1.32 {q10}, [r3,:128]! + vcvt.s32.f32 q10, q10, #31 + vld1.32 {q11}, [r3,:128]! + vcvt.s32.f32 q11, q11, #31 + vst1.16 {q13}, [r0,:128]! + bne 1b + ands r2, r2, #15 + beq 3f +2: vsri.32 q10, q8, #16 + vld1.32 {q0}, [r1,:128]! + vcvt.s32.f32 q0, q0, #31 + vld1.32 {q1}, [r1,:128]! + vcvt.s32.f32 q1, q1, #31 + vld1.32 {q12}, [r3,:128]! + vcvt.s32.f32 q12, q12, #31 + vsri.32 q11, q9, #16 + vld1.32 {q13}, [r3,:128]! + vcvt.s32.f32 q13, q13, #31 + vst1.16 {q10}, [r0,:128]! + vsri.32 q12, q0, #16 + vst1.16 {q11}, [r0,:128]! + vsri.32 q13, q1, #16 + vst1.16 {q12-q13},[r0,:128]! + bx lr +3: vsri.32 q10, q8, #16 + vsri.32 q11, q9, #16 + vst1.16 {q10-q11},[r0,:128]! + bx lr +endfunc + +function swri_oldapi_conv_fltp_to_s16_nch_neon, export=1 + cmp r3, #2 + itt lt + ldrlt r1, [r1] + blt swri_oldapi_conv_flt_to_s16_neon + beq swri_oldapi_conv_fltp_to_s16_2ch_neon + + push {r4-r8, lr} + cmp r3, #4 + lsl r12, r3, #1 + blt 4f + + @ 4 channels +5: ldm r1!, {r4-r7} + mov lr, r2 + mov r8, r0 + vld1.32 {q8}, [r4,:128]! + vcvt.s32.f32 q8, q8, #31 + vld1.32 {q9}, [r5,:128]! + vcvt.s32.f32 q9, q9, #31 + vld1.32 {q10}, [r6,:128]! + vcvt.s32.f32 q10, q10, #31 + vld1.32 {q11}, [r7,:128]! + vcvt.s32.f32 q11, q11, #31 +6: subs lr, lr, #8 + vld1.32 {q0}, [r4,:128]! + vcvt.s32.f32 q0, q0, #31 + vsri.32 q9, q8, #16 + vld1.32 {q1}, [r5,:128]! + vcvt.s32.f32 q1, q1, #31 + vsri.32 q11, q10, #16 + vld1.32 {q2}, [r6,:128]! + vcvt.s32.f32 q2, q2, #31 + vzip.32 d18, d22 + vld1.32 {q3}, [r7,:128]! + vcvt.s32.f32 q3, q3, #31 + vzip.32 d19, d23 + vst1.16 {d18}, [r8], r12 + vsri.32 q1, q0, #16 + vst1.16 {d22}, [r8], r12 + vsri.32 q3, q2, #16 + vst1.16 {d19}, [r8], r12 + vzip.32 d2, d6 + vst1.16 {d23}, [r8], r12 + vzip.32 d3, d7 + beq 7f + vld1.32 {q8}, [r4,:128]! + vcvt.s32.f32 q8, q8, #31 + vst1.16 {d2}, [r8], r12 + vld1.32 {q9}, [r5,:128]! + vcvt.s32.f32 q9, q9, #31 + vst1.16 {d6}, [r8], r12 + vld1.32 {q10}, [r6,:128]! + vcvt.s32.f32 q10, q10, #31 + vst1.16 {d3}, [r8], r12 + vld1.32 {q11}, [r7,:128]! + vcvt.s32.f32 q11, q11, #31 + vst1.16 {d7}, [r8], r12 + b 6b +7: vst1.16 {d2}, [r8], r12 + vst1.16 {d6}, [r8], r12 + vst1.16 {d3}, [r8], r12 + vst1.16 {d7}, [r8], r12 + subs r3, r3, #4 + it eq + popeq {r4-r8, pc} + cmp r3, #4 + add r0, r0, #8 + bge 5b + + @ 2 channels +4: cmp r3, #2 + blt 4f + ldm r1!, {r4-r5} + mov lr, r2 + mov r8, r0 + tst lr, #8 + vld1.32 {q8}, [r4,:128]! + vcvt.s32.f32 q8, q8, #31 + vld1.32 {q9}, [r5,:128]! + vcvt.s32.f32 q9, q9, #31 + vld1.32 {q10}, [r4,:128]! + vcvt.s32.f32 q10, q10, #31 + vld1.32 {q11}, [r5,:128]! + vcvt.s32.f32 q11, q11, #31 + beq 6f + subs lr, lr, #8 + beq 7f + vsri.32 d18, d16, #16 + vsri.32 d19, d17, #16 + vld1.32 {q8}, [r4,:128]! + vcvt.s32.f32 q8, q8, #31 + vst1.32 {d18[0]}, [r8], r12 + vsri.32 d22, d20, #16 + vst1.32 {d18[1]}, [r8], r12 + vsri.32 d23, d21, #16 + vst1.32 {d19[0]}, [r8], r12 + vst1.32 {d19[1]}, [r8], r12 + vld1.32 {q9}, [r5,:128]! + vcvt.s32.f32 q9, q9, #31 + vst1.32 {d22[0]}, [r8], r12 + vst1.32 {d22[1]}, [r8], r12 + vld1.32 {q10}, [r4,:128]! + vcvt.s32.f32 q10, q10, #31 + vst1.32 {d23[0]}, [r8], r12 + vst1.32 {d23[1]}, [r8], r12 + vld1.32 {q11}, [r5,:128]! + vcvt.s32.f32 q11, q11, #31 +6: subs lr, lr, #16 + vld1.32 {q0}, [r4,:128]! + vcvt.s32.f32 q0, q0, #31 + vsri.32 d18, d16, #16 + vld1.32 {q1}, [r5,:128]! + vcvt.s32.f32 q1, q1, #31 + vsri.32 d19, d17, #16 + vld1.32 {q2}, [r4,:128]! + vcvt.s32.f32 q2, q2, #31 + vld1.32 {q3}, [r5,:128]! + vcvt.s32.f32 q3, q3, #31 + vst1.32 {d18[0]}, [r8], r12 + vsri.32 d22, d20, #16 + vst1.32 {d18[1]}, [r8], r12 + vsri.32 d23, d21, #16 + vst1.32 {d19[0]}, [r8], r12 + vsri.32 d2, d0, #16 + vst1.32 {d19[1]}, [r8], r12 + vsri.32 d3, d1, #16 + vst1.32 {d22[0]}, [r8], r12 + vsri.32 d6, d4, #16 + vst1.32 {d22[1]}, [r8], r12 + vsri.32 d7, d5, #16 + vst1.32 {d23[0]}, [r8], r12 + vst1.32 {d23[1]}, [r8], r12 + beq 6f + vld1.32 {q8}, [r4,:128]! + vcvt.s32.f32 q8, q8, #31 + vst1.32 {d2[0]}, [r8], r12 + vst1.32 {d2[1]}, [r8], r12 + vld1.32 {q9}, [r5,:128]! + vcvt.s32.f32 q9, q9, #31 + vst1.32 {d3[0]}, [r8], r12 + vst1.32 {d3[1]}, [r8], r12 + vld1.32 {q10}, [r4,:128]! + vcvt.s32.f32 q10, q10, #31 + vst1.32 {d6[0]}, [r8], r12 + vst1.32 {d6[1]}, [r8], r12 + vld1.32 {q11}, [r5,:128]! + vcvt.s32.f32 q11, q11, #31 + vst1.32 {d7[0]}, [r8], r12 + vst1.32 {d7[1]}, [r8], r12 + bgt 6b +6: vst1.32 {d2[0]}, [r8], r12 + vst1.32 {d2[1]}, [r8], r12 + vst1.32 {d3[0]}, [r8], r12 + vst1.32 {d3[1]}, [r8], r12 + vst1.32 {d6[0]}, [r8], r12 + vst1.32 {d6[1]}, [r8], r12 + vst1.32 {d7[0]}, [r8], r12 + vst1.32 {d7[1]}, [r8], r12 + b 8f +7: vsri.32 d18, d16, #16 + vsri.32 d19, d17, #16 + vst1.32 {d18[0]}, [r8], r12 + vsri.32 d22, d20, #16 + vst1.32 {d18[1]}, [r8], r12 + vsri.32 d23, d21, #16 + vst1.32 {d19[0]}, [r8], r12 + vst1.32 {d19[1]}, [r8], r12 + vst1.32 {d22[0]}, [r8], r12 + vst1.32 {d22[1]}, [r8], r12 + vst1.32 {d23[0]}, [r8], r12 + vst1.32 {d23[1]}, [r8], r12 +8: subs r3, r3, #2 + add r0, r0, #4 + it eq + popeq {r4-r8, pc} + + @ 1 channel +4: ldr r4, [r1] + tst r2, #8 + mov lr, r2 + mov r5, r0 + vld1.32 {q0}, [r4,:128]! + vcvt.s32.f32 q0, q0, #31 + vld1.32 {q1}, [r4,:128]! + vcvt.s32.f32 q1, q1, #31 + bne 8f +6: subs lr, lr, #16 + vld1.32 {q2}, [r4,:128]! + vcvt.s32.f32 q2, q2, #31 + vld1.32 {q3}, [r4,:128]! + vcvt.s32.f32 q3, q3, #31 + vst1.16 {d0[1]}, [r5,:16], r12 + vst1.16 {d0[3]}, [r5,:16], r12 + vst1.16 {d1[1]}, [r5,:16], r12 + vst1.16 {d1[3]}, [r5,:16], r12 + vst1.16 {d2[1]}, [r5,:16], r12 + vst1.16 {d2[3]}, [r5,:16], r12 + vst1.16 {d3[1]}, [r5,:16], r12 + vst1.16 {d3[3]}, [r5,:16], r12 + beq 7f + vld1.32 {q0}, [r4,:128]! + vcvt.s32.f32 q0, q0, #31 + vld1.32 {q1}, [r4,:128]! + vcvt.s32.f32 q1, q1, #31 +7: vst1.16 {d4[1]}, [r5,:16], r12 + vst1.16 {d4[3]}, [r5,:16], r12 + vst1.16 {d5[1]}, [r5,:16], r12 + vst1.16 {d5[3]}, [r5,:16], r12 + vst1.16 {d6[1]}, [r5,:16], r12 + vst1.16 {d6[3]}, [r5,:16], r12 + vst1.16 {d7[1]}, [r5,:16], r12 + vst1.16 {d7[3]}, [r5,:16], r12 + bgt 6b + pop {r4-r8, pc} +8: subs lr, lr, #8 + vst1.16 {d0[1]}, [r5,:16], r12 + vst1.16 {d0[3]}, [r5,:16], r12 + vst1.16 {d1[1]}, [r5,:16], r12 + vst1.16 {d1[3]}, [r5,:16], r12 + vst1.16 {d2[1]}, [r5,:16], r12 + vst1.16 {d2[3]}, [r5,:16], r12 + vst1.16 {d3[1]}, [r5,:16], r12 + vst1.16 {d3[3]}, [r5,:16], r12 + it eq + popeq {r4-r8, pc} + vld1.32 {q0}, [r4,:128]! + vcvt.s32.f32 q0, q0, #31 + vld1.32 {q1}, [r4,:128]! + vcvt.s32.f32 q1, q1, #31 + b 6b +endfunc diff --git a/libswresample/audioconvert.c b/libswresample/audioconvert.c new file mode 100644 index 0000000000..d2e3722a40 --- /dev/null +++ b/libswresample/audioconvert.c @@ -0,0 +1,208 @@ +/* + * 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" + + +#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)\ +{\ + uint8_t *end2 = end - 3*os;\ + while(po < end2){\ + *(otype*)po = expr; pi += is; po += os;\ + *(otype*)po = expr; pi += is; po += os;\ + *(otype*)po = expr; pi += is; po += os;\ + *(otype*)po = expr; pi += is; po += os;\ + }\ + while(po < end){\ + *(otype*)po = expr; pi += is; po += os;\ + }\ +} + +//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.0f/ (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.0f/ (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.0f/ (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) + +static conv_func_type * const 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), +}; + +static void cpy1(uint8_t **dst, const uint8_t **src, int len){ + memcpy(*dst, *src, len); +} +static void cpy2(uint8_t **dst, const uint8_t **src, int len){ + memcpy(*dst, *src, 2*len); +} +static void cpy4(uint8_t **dst, const uint8_t **src, int len){ + memcpy(*dst, *src, 4*len); +} +static void cpy8(uint8_t **dst, const uint8_t **src, int len){ + memcpy(*dst, *src, 8*len); +} + +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[av_get_packed_sample_fmt(out_fmt) + AV_SAMPLE_FMT_NB*av_get_packed_sample_fmt(in_fmt)]; + + if (!f) + return NULL; + ctx = av_mallocz(sizeof(*ctx)); + if (!ctx) + return NULL; + + if(channels == 1){ + in_fmt = av_get_planar_sample_fmt( in_fmt); + out_fmt = av_get_planar_sample_fmt(out_fmt); + } + + ctx->channels = channels; + ctx->conv_f = f; + ctx->ch_map = ch_map; + if (in_fmt == AV_SAMPLE_FMT_U8 || in_fmt == AV_SAMPLE_FMT_U8P) + memset(ctx->silence, 0x80, sizeof(ctx->silence)); + + if(out_fmt == in_fmt && !ch_map) { + switch(av_get_bytes_per_sample(in_fmt)){ + case 1:ctx->simd_f = cpy1; break; + case 2:ctx->simd_f = cpy2; break; + case 4:ctx->simd_f = cpy4; break; + case 8:ctx->simd_f = cpy8; break; + } + } + + if(HAVE_YASM && HAVE_MMX) swri_audio_convert_init_x86(ctx, out_fmt, in_fmt, channels); + if(ARCH_ARM) swri_audio_convert_init_arm(ctx, out_fmt, in_fmt, channels); + + 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; + int off=0; + const int os= (out->planar ? 1 :out->ch_count) *out->bps; + + av_assert0(ctx->channels == out->ch_count); + + //FIXME optimize common cases + + if(ctx->simd_f && !ctx->ch_map){ + off = len&~15; + av_assert1(off>=0); + av_assert1(off<=len); + av_assert2(ctx->channels == SWR_CH_MAX || !in->ch[ctx->channels]); + if(off>0){ + if(out->planar == in->planar){ + int planes = out->planar ? out->ch_count : 1; + for(ch=0; ch<planes; ch++){ + ctx->simd_f(out->ch+ch, (const uint8_t **)in->ch+ch, off * (out->planar ? 1 :out->ch_count)); + } + }else{ + ctx->simd_f(out->ch, (const uint8_t **)in->ch, off); + } + } + if(off == len) + return 0; + } + + 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 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+off*os, pi+off*is, is, os, end); + } + return 0; +} diff --git a/libswresample/audioconvert.h b/libswresample/audioconvert.h new file mode 100644 index 0000000000..9a234d4b93 --- /dev/null +++ b/libswresample/audioconvert.h @@ -0,0 +1,77 @@ +/* + * 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" + + +typedef void (conv_func_type)(uint8_t *po, const uint8_t *pi, int is, int os, uint8_t *end); +typedef void (simd_func_type)(uint8_t **dst, const uint8_t **src, int len); + +typedef struct AudioConvert { + int channels; + conv_func_type *conv_f; + simd_func_type *simd_f; + const int *ch_map; + uint8_t silence[8]; ///< silence input sample +}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/dither.c b/libswresample/dither.c new file mode 100644 index 0000000000..79113f4c23 --- /dev/null +++ b/libswresample/dither.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 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/avassert.h" +#include "swresample_internal.h" + +void swri_get_dither(SwrContext *s, void *dst, int len, unsigned seed, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt) { + double scale = 0; +#define TMP_EXTRA 2 + double *tmp = av_malloc((len + TMP_EXTRA) * sizeof(double)); + int i; + + out_fmt = av_get_packed_sample_fmt(out_fmt); + in_fmt = av_get_packed_sample_fmt( in_fmt); + + if(in_fmt == AV_SAMPLE_FMT_FLT || in_fmt == AV_SAMPLE_FMT_DBL){ + if(out_fmt == AV_SAMPLE_FMT_S32) scale = 1.0/(1L<<31); + if(out_fmt == AV_SAMPLE_FMT_S16) scale = 1.0/(1L<<15); + if(out_fmt == AV_SAMPLE_FMT_U8 ) scale = 1.0/(1L<< 7); + } + if(in_fmt == AV_SAMPLE_FMT_S32 && out_fmt == AV_SAMPLE_FMT_S16) scale = 1L<<16; + if(in_fmt == AV_SAMPLE_FMT_S32 && out_fmt == AV_SAMPLE_FMT_U8 ) scale = 1L<<24; + if(in_fmt == AV_SAMPLE_FMT_S16 && out_fmt == AV_SAMPLE_FMT_U8 ) scale = 1L<<8; + + scale *= s->dither_scale; + + for(i=0; i<len + TMP_EXTRA; i++){ + double v; + seed = seed* 1664525 + 1013904223; + + switch(s->dither_method){ + case SWR_DITHER_RECTANGULAR: v= ((double)seed) / UINT_MAX - 0.5; break; + case SWR_DITHER_TRIANGULAR : + case SWR_DITHER_TRIANGULAR_HIGHPASS : + v = ((double)seed) / UINT_MAX; + seed = seed*1664525 + 1013904223; + v-= ((double)seed) / UINT_MAX; + break; + default: av_assert0(0); + } + tmp[i] = v; + } + + for(i=0; i<len; i++){ + double v; + + switch(s->dither_method){ + case SWR_DITHER_RECTANGULAR: + case SWR_DITHER_TRIANGULAR : + v = tmp[i]; + break; + case SWR_DITHER_TRIANGULAR_HIGHPASS : + v = (- tmp[i] + 2*tmp[i+1] - tmp[i+2]) / sqrt(6); + break; + default: av_assert0(0); + } + + v*= scale; + + switch(in_fmt){ + case AV_SAMPLE_FMT_S16: ((int16_t*)dst)[i] = v; break; + case AV_SAMPLE_FMT_S32: ((int32_t*)dst)[i] = v; break; + case AV_SAMPLE_FMT_FLT: ((float *)dst)[i] = v; break; + case AV_SAMPLE_FMT_DBL: ((double *)dst)[i] = v; break; + default: av_assert0(0); + } + } + + av_free(tmp); +} diff --git a/libswresample/libswresample.v b/libswresample/libswresample.v new file mode 100644 index 0000000000..9b797bde77 --- /dev/null +++ b/libswresample/libswresample.v @@ -0,0 +1,4 @@ +LIBSWRESAMPLE_$MAJOR { + global: swr_*; ff_*; swresample_*; + local: *; +}; diff --git a/libswresample/rematrix.c b/libswresample/rematrix.c new file mode 100644 index 0000000000..72da438e6c --- /dev/null +++ b/libswresample/rematrix.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2011-2012 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 INTER float +#define RENAME(x) x ## _float +#include "rematrix_template.c" +#undef SAMPLE +#undef RENAME +#undef R +#undef ONE +#undef COEFF +#undef INTER + +#define ONE (1.0) +#define R(x) x +#define SAMPLE double +#define COEFF double +#define INTER double +#define RENAME(x) x ## _double +#include "rematrix_template.c" +#undef SAMPLE +#undef RENAME +#undef R +#undef ONE +#undef COEFF +#undef INTER + +#define ONE (-32768) +#define R(x) (((x) + 16384)>>15) +#define SAMPLE int16_t +#define COEFF int +#define INTER 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 + +int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride) +{ + int nb_in, nb_out, in, out; + + if (!s || s->in_convert) // s needs to be allocated but not initialized + return AVERROR(EINVAL); + memset(s->matrix, 0, sizeof(s->matrix)); + nb_in = av_get_channel_layout_nb_channels(s->in_ch_layout); + nb_out = av_get_channel_layout_nb_channels(s->out_ch_layout); + for (out = 0; out < nb_out; out++) { + for (in = 0; in < nb_in; in++) + s->matrix[out][in] = matrix[in]; + matrix += stride; + } + s->rematrix_custom = 1; + return 0; +} + +static int even(int64_t layout){ + if(!layout) return 1; + if(layout&(layout-1)) return 1; + return 0; +} + +static int clean_layout(SwrContext *s, int64_t layout){ + if((layout & AV_CH_LAYOUT_STEREO_DOWNMIX) == AV_CH_LAYOUT_STEREO_DOWNMIX) + return AV_CH_LAYOUT_STEREO; + + if(layout && layout != AV_CH_FRONT_CENTER && !(layout&(layout-1))) { + char buf[128]; + av_get_channel_layout_string(buf, sizeof(buf), -1, layout); + av_log(s, AV_LOG_VERBOSE, "Treating %s as mono\n", buf); + return AV_CH_FRONT_CENTER; + } + + return layout; +} + +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; +} + +av_cold static int auto_matrix(SwrContext *s) +{ + int i, j, out_i; + double matrix[64][64]={{0}}; + int64_t unaccounted, in_ch_layout, out_ch_layout; + double maxcoef=0; + char buf[128]; + const int matrix_encoding = s->matrix_encoding; + + in_ch_layout = clean_layout(s, s->in_ch_layout); + if(!sane_layout(in_ch_layout)){ + av_get_channel_layout_string(buf, sizeof(buf), -1, s->in_ch_layout); + av_log(s, AV_LOG_ERROR, "Input channel layout '%s' is not supported\n", buf); + return AVERROR(EINVAL); + } + + out_ch_layout = clean_layout(s, s->out_ch_layout); + if(!sane_layout(out_ch_layout)){ + av_get_channel_layout_string(buf, sizeof(buf), -1, s->out_ch_layout); + av_log(s, AV_LOG_ERROR, "Output channel layout '%s' is not supported\n", buf); + return AVERROR(EINVAL); + } + + memset(s->matrix, 0, sizeof(s->matrix)); + for(i=0; i<64; i++){ + if(in_ch_layout & out_ch_layout & (1LL<<i)) + matrix[i][i]= 1.0; + } + + unaccounted= in_ch_layout & ~out_ch_layout; + +//FIXME implement dolby surround +//FIXME implement full ac3 + + + if(unaccounted & AV_CH_FRONT_CENTER){ + if((out_ch_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO){ + if(in_ch_layout & AV_CH_LAYOUT_STEREO) { + matrix[ FRONT_LEFT][FRONT_CENTER]+= s->clev; + matrix[FRONT_RIGHT][FRONT_CENTER]+= s->clev; + } else { + 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(out_ch_layout & AV_CH_FRONT_CENTER){ + matrix[FRONT_CENTER][ FRONT_LEFT]+= M_SQRT1_2; + matrix[FRONT_CENTER][FRONT_RIGHT]+= M_SQRT1_2; + if(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(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(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(out_ch_layout & AV_CH_FRONT_LEFT){ + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY || + matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + if (unaccounted & (AV_CH_BACK_LEFT | AV_CH_SIDE_LEFT)) { + matrix[FRONT_LEFT ][BACK_CENTER] -= s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_CENTER] += s->slev * M_SQRT1_2; + } else { + matrix[FRONT_LEFT ][BACK_CENTER] -= s->slev; + matrix[FRONT_RIGHT][BACK_CENTER] += s->slev; + } + } else { + matrix[ FRONT_LEFT][BACK_CENTER]+= s->slev*M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_CENTER]+= s->slev*M_SQRT1_2; + } + }else if(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(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(out_ch_layout & AV_CH_SIDE_LEFT){ + if(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(out_ch_layout & AV_CH_FRONT_LEFT){ + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[FRONT_LEFT ][BACK_LEFT ] -= s->slev * M_SQRT1_2; + matrix[FRONT_LEFT ][BACK_RIGHT] -= s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_LEFT ] += s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_RIGHT] += s->slev * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + matrix[FRONT_LEFT ][BACK_LEFT ] -= s->slev * SQRT3_2; + matrix[FRONT_LEFT ][BACK_RIGHT] -= s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_LEFT ] += s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_RIGHT] += s->slev * SQRT3_2; + } else { + matrix[ FRONT_LEFT][ BACK_LEFT] += s->slev; + matrix[FRONT_RIGHT][BACK_RIGHT] += s->slev; + } + }else if(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(out_ch_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_ch_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_ch_layout & AV_CH_BACK_CENTER){ + matrix[BACK_CENTER][ SIDE_LEFT]+= M_SQRT1_2; + matrix[BACK_CENTER][SIDE_RIGHT]+= M_SQRT1_2; + }else if(out_ch_layout & AV_CH_FRONT_LEFT){ + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[FRONT_LEFT ][SIDE_LEFT ] -= s->slev * M_SQRT1_2; + matrix[FRONT_LEFT ][SIDE_RIGHT] -= s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_LEFT ] += s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_RIGHT] += s->slev * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + matrix[FRONT_LEFT ][SIDE_LEFT ] -= s->slev * SQRT3_2; + matrix[FRONT_LEFT ][SIDE_RIGHT] -= s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_LEFT ] += s->slev * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_RIGHT] += s->slev * SQRT3_2; + } else { + matrix[ FRONT_LEFT][ SIDE_LEFT] += s->slev; + matrix[FRONT_RIGHT][SIDE_RIGHT] += s->slev; + } + }else if(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(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(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); + } + /* mix LFE into front left/right or center */ + if (unaccounted & AV_CH_LOW_FREQUENCY) { + if (out_ch_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][LOW_FREQUENCY] += s->lfe_mix_level; + } else if (out_ch_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][LOW_FREQUENCY] += s->lfe_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][LOW_FREQUENCY] += s->lfe_mix_level * M_SQRT1_2; + } else + av_assert0(0); + } + + for(out_i=i=0; i<64; i++){ + double sum=0; + int in_i=0; + for(j=0; j<64; j++){ + s->matrix[out_i][in_i]= matrix[i][j]; + if(matrix[i][j]){ + sum += fabs(matrix[i][j]); + } + if(in_ch_layout & (1ULL<<j)) + in_i++; + } + maxcoef= FFMAX(maxcoef, sum); + if(out_ch_layout & (1ULL<<i)) + out_i++; + } + if(s->rematrix_volume < 0) + maxcoef = -s->rematrix_volume; + + if(( av_get_packed_sample_fmt(s->out_sample_fmt) < AV_SAMPLE_FMT_FLT + || av_get_packed_sample_fmt(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; + } + } + + 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; + } + } + + for(i=0; i<av_get_channel_layout_nb_channels(out_ch_layout); i++){ + for(j=0; j<av_get_channel_layout_nb_channels(in_ch_layout); j++){ + av_log(NULL, AV_LOG_DEBUG, "%f ", s->matrix[i][j]); + } + av_log(NULL, AV_LOG_DEBUG, "\n"); + } + return 0; +} + +av_cold int swri_rematrix_init(SwrContext *s){ + int i, j; + int nb_in = av_get_channel_layout_nb_channels(s->in_ch_layout); + int nb_out = av_get_channel_layout_nb_channels(s->out_ch_layout); + + s->mix_any_f = NULL; + + if (!s->rematrix_custom) { + int r = auto_matrix(s); + if (r) + return r; + } + if (s->midbuf.fmt == AV_SAMPLE_FMT_S16P){ + s->native_matrix = av_mallocz(nb_in * nb_out * sizeof(int)); + s->native_one = av_mallocz(sizeof(int)); + for (i = 0; i < nb_out; i++) + for (j = 0; j < nb_in; j++) + ((int*)s->native_matrix)[i * nb_in + j] = lrintf(s->matrix[i][j] * 32768); + *((int*)s->native_one) = 32768; + s->mix_1_1_f = (mix_1_1_func_type*)copy_s16; + s->mix_2_1_f = (mix_2_1_func_type*)sum2_s16; + s->mix_any_f = (mix_any_func_type*)get_mix_any_func_s16(s); + }else if(s->midbuf.fmt == AV_SAMPLE_FMT_FLTP){ + s->native_matrix = av_mallocz(nb_in * nb_out * sizeof(float)); + s->native_one = av_mallocz(sizeof(float)); + for (i = 0; i < nb_out; i++) + for (j = 0; j < nb_in; j++) + ((float*)s->native_matrix)[i * nb_in + j] = s->matrix[i][j]; + *((float*)s->native_one) = 1.0; + s->mix_1_1_f = (mix_1_1_func_type*)copy_float; + s->mix_2_1_f = (mix_2_1_func_type*)sum2_float; + s->mix_any_f = (mix_any_func_type*)get_mix_any_func_float(s); + }else if(s->midbuf.fmt == AV_SAMPLE_FMT_DBLP){ + s->native_matrix = av_mallocz(nb_in * nb_out * sizeof(double)); + s->native_one = av_mallocz(sizeof(double)); + for (i = 0; i < nb_out; i++) + for (j = 0; j < nb_in; j++) + ((double*)s->native_matrix)[i * nb_in + j] = s->matrix[i][j]; + *((double*)s->native_one) = 1.0; + s->mix_1_1_f = (mix_1_1_func_type*)copy_double; + s->mix_2_1_f = (mix_2_1_func_type*)sum2_double; + s->mix_any_f = (mix_any_func_type*)get_mix_any_func_double(s); + }else + av_assert0(0); + //FIXME quantize for integeres + for (i = 0; i < SWR_CH_MAX; i++) { + int ch_in=0; + for (j = 0; j < SWR_CH_MAX; j++) { + s->matrix32[i][j]= lrintf(s->matrix[i][j] * 32768); + if(s->matrix[i][j]) + s->matrix_ch[i][++ch_in]= j; + } + s->matrix_ch[i][0]= ch_in; + } + + if(HAVE_YASM && HAVE_MMX) swri_rematrix_init_x86(s); + + return 0; +} + +av_cold void swri_rematrix_free(SwrContext *s){ + av_freep(&s->native_matrix); + av_freep(&s->native_one); + av_freep(&s->native_simd_matrix); +} + +int swri_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy){ + int out_i, in_i, i, j; + int len1 = 0; + int off = 0; + + if(s->mix_any_f) { + s->mix_any_f(out->ch, (const uint8_t **)in->ch, s->native_matrix, len); + return 0; + } + + if(s->mix_2_1_simd || s->mix_1_1_simd){ + len1= len&~15; + off = len1 * out->bps; + } + + 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 0: + if(mustcopy) + memset(out->ch[out_i], 0, len * av_get_bytes_per_sample(s->int_sample_fmt)); + break; + case 1: + in_i= s->matrix_ch[out_i][1]; + if(s->matrix[out_i][in_i]!=1.0){ + if(s->mix_1_1_simd && len1) + s->mix_1_1_simd(out->ch[out_i] , in->ch[in_i] , s->native_simd_matrix, in->ch_count*out_i + in_i, len1); + if(len != len1) + s->mix_1_1_f (out->ch[out_i]+off, in->ch[in_i]+off, s->native_matrix, in->ch_count*out_i + in_i, len-len1); + }else if(mustcopy){ + memcpy(out->ch[out_i], in->ch[in_i], len*out->bps); + }else{ + out->ch[out_i]= in->ch[in_i]; + } + break; + case 2: { + int in_i1 = s->matrix_ch[out_i][1]; + int in_i2 = s->matrix_ch[out_i][2]; + if(s->mix_2_1_simd && len1) + s->mix_2_1_simd(out->ch[out_i] , in->ch[in_i1] , in->ch[in_i2] , s->native_simd_matrix, in->ch_count*out_i + in_i1, in->ch_count*out_i + in_i2, len1); + else + s->mix_2_1_f (out->ch[out_i] , in->ch[in_i1] , in->ch[in_i2] , s->native_matrix, in->ch_count*out_i + in_i1, in->ch_count*out_i + in_i2, len1); + if(len != len1) + s->mix_2_1_f (out->ch[out_i]+off, in->ch[in_i1]+off, in->ch[in_i2]+off, s->native_matrix, in->ch_count*out_i + in_i1, in->ch_count*out_i + in_i2, len-len1); + break;} + default: + if(s->int_sample_fmt == AV_SAMPLE_FMT_FLTP){ + 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 if(s->int_sample_fmt == AV_SAMPLE_FMT_DBLP){ + for(i=0; i<len; i++){ + double v=0; + for(j=0; j<s->matrix_ch[out_i][0]; j++){ + in_i= s->matrix_ch[out_i][1+j]; + v+= ((double*)in->ch[in_i])[i] * s->matrix[out_i][in_i]; + } + ((double*)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..627bf1f6fc --- /dev/null +++ b/libswresample/rematrix_template.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011-2012 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 + */ + +typedef void (RENAME(mix_any_func_type))(SAMPLE **out, const SAMPLE **in1, COEFF *coeffp, int len); + +static void RENAME(sum2)(SAMPLE *out, const SAMPLE *in1, const SAMPLE *in2, COEFF *coeffp, int index1, int index2, int len){ + int i; + COEFF coeff1 = coeffp[index1]; + COEFF coeff2 = coeffp[index2]; + + 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 *coeffp, int index, int len){ + int i; + COEFF coeff = coeffp[index]; + for(i=0; i<len; i++) + out[i] = R(coeff*in[i]); +} + +static void RENAME(mix6to2)(SAMPLE **out, const SAMPLE **in, COEFF *coeffp, int len){ + int i; + + for(i=0; i<len; i++) { + INTER t = in[2][i]*coeffp[0*6+2] + in[3][i]*coeffp[0*6+3]; + out[0][i] = R(t + in[0][i]*coeffp[0*6+0] + in[4][i]*coeffp[0*6+4]); + out[1][i] = R(t + in[1][i]*coeffp[1*6+1] + in[5][i]*coeffp[1*6+5]); + } +} + +static void RENAME(mix8to2)(SAMPLE **out, const SAMPLE **in, COEFF *coeffp, int len){ + int i; + + for(i=0; i<len; i++) { + INTER t = in[2][i]*coeffp[0*8+2] + in[3][i]*coeffp[0*8+3]; + out[0][i] = R(t + in[0][i]*coeffp[0*8+0] + in[4][i]*coeffp[0*8+4] + in[6][i]*coeffp[0*8+6]); + out[1][i] = R(t + in[1][i]*coeffp[1*8+1] + in[5][i]*coeffp[1*8+5] + in[7][i]*coeffp[1*8+7]); + } +} + +static RENAME(mix_any_func_type) *RENAME(get_mix_any_func)(SwrContext *s){ + if( s->out_ch_layout == AV_CH_LAYOUT_STEREO && (s->in_ch_layout == AV_CH_LAYOUT_5POINT1 || s->in_ch_layout == AV_CH_LAYOUT_5POINT1_BACK) + && s->matrix[0][2] == s->matrix[1][2] && s->matrix[0][3] == s->matrix[1][3] + && !s->matrix[0][1] && !s->matrix[0][5] && !s->matrix[1][0] && !s->matrix[1][4] + ) + return RENAME(mix6to2); + + if( s->out_ch_layout == AV_CH_LAYOUT_STEREO && s->in_ch_layout == AV_CH_LAYOUT_7POINT1 + && s->matrix[0][2] == s->matrix[1][2] && s->matrix[0][3] == s->matrix[1][3] + && !s->matrix[0][1] && !s->matrix[0][5] && !s->matrix[1][0] && !s->matrix[1][4] + && !s->matrix[0][7] && !s->matrix[1][6] + ) + return RENAME(mix8to2); + + return NULL; +} diff --git a/libswresample/resample.c b/libswresample/resample.c new file mode 100644 index 0000000000..bf412dd268 --- /dev/null +++ b/libswresample/resample.c @@ -0,0 +1,459 @@ +/* + * audio resampling + * Copyright (c) 2004-2012 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 "libavutil/avassert.h" +#include "swresample_internal.h" + + +typedef struct ResampleContext { + const AVClass *av_class; + uint8_t *filter_bank; + int filter_length; + int filter_alloc; + 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; + enum SwrFilterType filter_type; + int kaiser_beta; + double factor; + enum AVSampleFormat format; + int felem_size; + int filter_shift; +} 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; + av_assert2(i<99); + } + return v; +} + +/** + * builds a polyphase filterbank. + * @param factor resampling factor + * @param scale wanted sum of coefficients for each filter + * @param filter_type filter type + * @param kaiser_beta kaiser window beta + * @return 0 on success, negative on error + */ +static int build_filter(ResampleContext *c, void *filter, double factor, int tap_count, int alloc, int phase_count, int scale, + int filter_type, int kaiser_beta){ + 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(filter_type){ + case SWR_FILTER_TYPE_CUBIC:{ + 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 SWR_FILTER_TYPE_BLACKMAN_NUTTALL: + 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; + case SWR_FILTER_TYPE_KAISER: + w = 2.0*x / (factor*tap_count*M_PI); + y *= bessel(kaiser_beta*sqrt(FFMAX(1-w*w, 0))); + break; + default: + av_assert0(0); + } + + tab[i] = y; + norm += y; + } + + /* normalize so that an uniform color remains the same */ + switch(c->format){ + case AV_SAMPLE_FMT_S16P: + for(i=0;i<tap_count;i++) + ((int16_t*)filter)[ph * alloc + i] = av_clip(lrintf(tab[i] * scale / norm), INT16_MIN, INT16_MAX); + break; + case AV_SAMPLE_FMT_S32P: + for(i=0;i<tap_count;i++) + ((int32_t*)filter)[ph * alloc + i] = av_clip(lrintf(tab[i] * scale / norm), INT32_MIN, INT32_MAX); + break; + case AV_SAMPLE_FMT_FLTP: + for(i=0;i<tap_count;i++) + ((float*)filter)[ph * alloc + i] = tab[i] * scale / norm; + break; + case AV_SAMPLE_FMT_DBLP: + for(i=0;i<tap_count;i++) + ((double*)filter)[ph * alloc + i] = tab[i] * scale / norm; + break; + } + } +#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, enum AVSampleFormat format, enum SwrFilterType filter_type, int kaiser_beta){ + 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->format != format + || c->filter_type != filter_type || c->kaiser_beta != kaiser_beta) { + c = av_mallocz(sizeof(*c)); + if (!c) + return NULL; + + c->format= format; + + c->felem_size= av_get_bytes_per_sample(c->format); + + switch(c->format){ + case AV_SAMPLE_FMT_S16P: + c->filter_shift = 15; + break; + case AV_SAMPLE_FMT_S32P: + c->filter_shift = 30; + break; + case AV_SAMPLE_FMT_FLTP: + case AV_SAMPLE_FMT_DBLP: + c->filter_shift = 0; + break; + default: + av_log(NULL, AV_LOG_ERROR, "Unsupported sample format\n"); + av_assert0(0); + } + + 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_alloc = FFALIGN(c->filter_length, 8); + c->filter_bank = av_mallocz(c->filter_alloc*(phase_count+1)*c->felem_size); + c->filter_type = filter_type; + c->kaiser_beta = kaiser_beta; + if (!c->filter_bank) + goto error; + if (build_filter(c, (void*)c->filter_bank, factor, c->filter_length, c->filter_alloc, phase_count, 1<<c->filter_shift, filter_type, kaiser_beta)) + goto error; + memcpy(c->filter_bank + (c->filter_alloc*phase_count+1)*c->felem_size, c->filter_bank, (c->filter_alloc-1)*c->felem_size); + memcpy(c->filter_bank + (c->filter_alloc*phase_count )*c->felem_size, c->filter_bank + (c->filter_alloc - 1)*c->felem_size, c->felem_size); + } + + 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); +} + +int swr_set_compensation(struct SwrContext *s, int sample_delta, int compensation_distance){ + ResampleContext *c; + int ret; + + if (!s || compensation_distance < 0) + return AVERROR(EINVAL); + if (!compensation_distance && sample_delta) + return AVERROR(EINVAL); + if (!s->resample) { + s->flags |= SWR_FLAG_RESAMPLE; + ret = swr_init(s); + if (ret < 0) + return ret; + } + c= s->resample; + c->compensation_distance= compensation_distance; + if (compensation_distance) + c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * (int64_t)sample_delta / compensation_distance; + else + c->dst_incr = c->ideal_dst_incr; + return 0; +} + +#define RENAME(N) N ## _int16 +#define FILTER_SHIFT 15 +#define DELEM int16_t +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define FELEM_MAX INT16_MAX +#define FELEM_MIN INT16_MIN +#define OUT(d, v) v = (v + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;\ + d = (unsigned)(v + 32768) > 65535 ? (v>>31) ^ 32767 : v +#include "resample_template.c" + +#undef RENAME +#undef FELEM +#undef FELEM2 +#undef DELEM +#undef FELEML +#undef OUT +#undef FELEM_MIN +#undef FELEM_MAX +#undef FILTER_SHIFT + + +#define RENAME(N) N ## _int32 +#define FILTER_SHIFT 30 +#define DELEM int32_t +#define FELEM int32_t +#define FELEM2 int64_t +#define FELEML int64_t +#define FELEM_MAX INT32_MAX +#define FELEM_MIN INT32_MIN +#define OUT(d, v) v = (v + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;\ + d = (uint64_t)(v + 0x80000000) > 0xFFFFFFFF ? (v>>63) ^ 0x7FFFFFFF : v +#include "resample_template.c" + +#undef RENAME +#undef FELEM +#undef FELEM2 +#undef DELEM +#undef FELEML +#undef OUT +#undef FELEM_MIN +#undef FELEM_MAX +#undef FILTER_SHIFT + + +#define RENAME(N) N ## _float +#define FILTER_SHIFT 0 +#define DELEM float +#define FELEM float +#define FELEM2 float +#define FELEML float +#define OUT(d, v) d = v +#include "resample_template.c" + +#undef RENAME +#undef FELEM +#undef FELEM2 +#undef DELEM +#undef FELEML +#undef OUT +#undef FELEM_MIN +#undef FELEM_MAX +#undef FILTER_SHIFT + + +#define RENAME(N) N ## _double +#define FILTER_SHIFT 0 +#define DELEM double +#define FELEM double +#define FELEM2 double +#define FELEML double +#define OUT(d, v) d = v +#include "resample_template.c" + +#undef RENAME +#undef FELEM +#undef FELEM2 +#undef DELEM +#undef FELEML +#undef OUT +#undef FELEM_MIN +#undef FELEM_MAX +#undef FILTER_SHIFT + +// XXX FIXME the whole C loop should be written in asm so this x86 specific code here isnt needed +#if HAVE_MMXEXT_INLINE +#include "x86/resample_mmx.h" +#define COMMON_CORE COMMON_CORE_INT16_MMX2 +#define RENAME(N) N ## _int16_mmx2 +#define FILTER_SHIFT 15 +#define DELEM int16_t +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define FELEM_MAX INT16_MAX +#define FELEM_MIN INT16_MIN +#define OUT(d, v) v = (v + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;\ + d = (unsigned)(v + 32768) > 65535 ? (v>>31) ^ 32767 : v +#include "resample_template.c" + +#undef COMMON_CORE +#undef RENAME +#undef FELEM +#undef FELEM2 +#undef DELEM +#undef FELEML +#undef OUT +#undef FELEM_MIN +#undef FELEM_MAX +#undef FILTER_SHIFT + +#if HAVE_SSSE3_INLINE +#define COMMON_CORE COMMON_CORE_INT16_SSSE3 +#define RENAME(N) N ## _int16_ssse3 +#define FILTER_SHIFT 15 +#define DELEM int16_t +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define FELEM_MAX INT16_MAX +#define FELEM_MIN INT16_MIN +#define OUT(d, v) v = (v + (1<<(FILTER_SHIFT-1)))>>FILTER_SHIFT;\ + d = (unsigned)(v + 32768) > 65535 ? (v>>31) ^ 32767 : v +#include "resample_template.c" +#endif +#endif // HAVE_MMXEXT_INLINE + +int swri_multiple_resample(ResampleContext *c, AudioData *dst, int dst_size, AudioData *src, int src_size, int *consumed){ + int i, ret= -1; + int av_unused mm_flags = av_get_cpu_flags(); + int need_emms= 0; + + for(i=0; i<dst->ch_count; i++){ +#if HAVE_MMXEXT_INLINE +#if HAVE_SSSE3_INLINE + if(c->format == AV_SAMPLE_FMT_S16P && (mm_flags&AV_CPU_FLAG_SSSE3)) ret= swri_resample_int16_ssse3(c, (int16_t*)dst->ch[i], (const int16_t*)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + else +#endif + if(c->format == AV_SAMPLE_FMT_S16P && (mm_flags&AV_CPU_FLAG_MMX2 )){ + ret= swri_resample_int16_mmx2 (c, (int16_t*)dst->ch[i], (const int16_t*)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + need_emms= 1; + } else +#endif + if(c->format == AV_SAMPLE_FMT_S16P) ret= swri_resample_int16(c, (int16_t*)dst->ch[i], (const int16_t*)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + else if(c->format == AV_SAMPLE_FMT_S32P) ret= swri_resample_int32(c, (int32_t*)dst->ch[i], (const int32_t*)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + else if(c->format == AV_SAMPLE_FMT_FLTP) ret= swri_resample_float(c, (float *)dst->ch[i], (const float *)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + else if(c->format == AV_SAMPLE_FMT_DBLP) ret= swri_resample_double(c,(double *)dst->ch[i], (const double *)src->ch[i], consumed, src_size, dst_size, i+1==dst->ch_count); + } + if(need_emms) + emms_c(); + return ret; +} + +int64_t swr_get_delay(struct SwrContext *s, int64_t base){ + ResampleContext *c = s->resample; + if(c){ + int64_t num = s->in_buffer_count - (c->filter_length-1)/2; + num <<= c->phase_shift; + num -= c->index; + num *= c->src_incr; + num -= c->frac; + + return av_rescale(num, base, s->in_sample_rate*(int64_t)c->src_incr << c->phase_shift); + }else{ + return (s->in_buffer_count*base + (s->in_sample_rate>>1))/ s->in_sample_rate; + } +} diff --git a/libswresample/resample_template.c b/libswresample/resample_template.c new file mode 100644 index 0000000000..ad840702ce --- /dev/null +++ b/libswresample/resample_template.c @@ -0,0 +1,139 @@ +/* + * audio resampling + * Copyright (c) 2004-2012 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> + */ + +int RENAME(swri_resample)(ResampleContext *c, DELEM *dst, const DELEM *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; + + av_assert1(c->filter_shift == FILTER_SHIFT); + av_assert1(c->felem_size == sizeof(FELEM)); + + 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 if(compensation_distance == 0 && !c->linear && index >= 0){ + for(dst_index=0; dst_index < dst_size; dst_index++){ + FELEM *filter= ((FELEM*)c->filter_bank) + c->filter_alloc*(index & c->phase_mask); + int sample_index= index >> c->phase_shift; + + if(sample_index + c->filter_length > src_size){ + break; + }else{ +#ifdef COMMON_CORE + COMMON_CORE +#else + FELEM2 val=0; + for(i=0; i<c->filter_length; i++){ + val += src[sample_index + i] * (FELEM2)filter[i]; + } + OUT(dst[dst_index], val); +#endif + } + + frac += dst_incr_frac; + index += dst_incr; + if(frac >= c->src_incr){ + frac -= c->src_incr; + index++; + } + } + }else{ + for(dst_index=0; dst_index < dst_size; dst_index++){ + FELEM *filter= ((FELEM*)c->filter_bank) + c->filter_alloc*(index & c->phase_mask); + int sample_index= index >> c->phase_shift; + FELEM2 val=0; + + if(sample_index + c->filter_length > src_size || -sample_index >= src_size){ + break; + }else if(sample_index < 0){ + for(i=0; i<c->filter_length; i++) + val += src[FFABS(sample_index + i)] * filter[i]; + }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_alloc]; + } + 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]; + } + } + + OUT(dst[dst_index], val); + + 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; + av_assert1(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; +} diff --git a/libswresample/swresample-test.c b/libswresample/swresample-test.c new file mode 100644 index 0000000000..4a0728c8e7 --- /dev/null +++ b/libswresample/swresample-test.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2011-2012 Michael Niedermayer (michaelni@gmx.at) + * Copyright (c) 2002 Fabrice Bellard + * + * 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 "libavutil/opt.h" +#include "swresample.h" + +#undef time +#include "time.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]/127.0-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]= av_clip_uint8 (lrint((v+1.0)*127)); break; + case AV_SAMPLE_FMT_S16: ((int16_t*)p)[index]= av_clip_int16 (lrint(v*32767)); break; + case AV_SAMPLE_FMT_S32: ((int32_t*)p)[index]= av_clipl_int32(lrint(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); + } +} + +static void shift(uint8_t *a[], int index, int ch_count, enum AVSampleFormat f){ + int ch; + + if(av_sample_fmt_is_planar(f)){ + f= av_get_alt_sample_fmt(f, 0); + for(ch= 0; ch<ch_count; ch++) + a[ch] += index*av_get_bytes_per_sample(f); + }else{ + a[0] += index*ch_count*av_get_bytes_per_sample(f); + } +} + +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[] = { + 8000, + 11025, + 16000, + 22050, + 32000, + 48000, +}; + +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 , +}; + +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; + } +} + +static int cmp(const int *a, const int *b){ + return *a - *b; +} + +static void audiogen(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[SWR_CH_MAX]; + double tabf2[SWR_CH_MAX]; + double taba[SWR_CH_MAX]; + unsigned static rnd; + +#define PUT_SAMPLE set(data, ch, k, channels, sample_fmt, v); +#define uint_rand(x) (x = x * 1664525 + 1013904223) +#define dbl_rand(x) (uint_rand(x)*2.0 / (double)UINT_MAX - 1) + 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 + uint_rand(rnd) % 5000; + tabf2[ch] = 100 + uint_rand(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; + } + } +} + +int main(int argc, char **argv){ + int in_sample_rate, out_sample_rate, ch ,i, flush_count; + uint64_t in_ch_layout, out_ch_layout; + enum AVSampleFormat in_sample_fmt, out_sample_fmt; + 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]; + int flush_i=0; + int mode; + int num_tests = 10000; + uint32_t seed = 0; + uint32_t rand_seed = 0; + int remaining_tests[FF_ARRAY_ELEMS(rates) * FF_ARRAY_ELEMS(layouts) * FF_ARRAY_ELEMS(formats) * FF_ARRAY_ELEMS(layouts) * FF_ARRAY_ELEMS(formats)]; + int max_tests = FF_ARRAY_ELEMS(remaining_tests); + int test; + int specific_test= -1; + + struct SwrContext * forw_ctx= NULL; + struct SwrContext *backw_ctx= NULL; + + if (argc > 1) { + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + av_log(NULL, AV_LOG_INFO, "Usage: swresample-test [<num_tests>[ <test>]] \n" + "num_tests Default is %d\n", num_tests); + return 0; + } + num_tests = strtol(argv[1], NULL, 0); + if(num_tests < 0) { + num_tests = -num_tests; + rand_seed = time(0); + } + if(num_tests<= 0 || num_tests>max_tests) + num_tests = max_tests; + if(argc > 2) { + specific_test = strtol(argv[1], NULL, 0); + } + } + + for(i=0; i<max_tests; i++) + remaining_tests[i] = i; + + for(test=0; test<num_tests; test++){ + unsigned r; + uint_rand(seed); + r = (seed * (uint64_t)(max_tests - test)) >>32; + FFSWAP(int, remaining_tests[r], remaining_tests[max_tests - test - 1]); + } + qsort(remaining_tests + max_tests - num_tests, num_tests, sizeof(remaining_tests[0]), (void*)cmp); + in_sample_rate=16000; + for(test=0; test<num_tests; test++){ + char in_layout_string[256]; + char out_layout_string[256]; + unsigned vector= remaining_tests[max_tests - test - 1]; + int in_ch_count; + int out_count, mid_count, out_ch_count; + + in_ch_layout = layouts[vector % FF_ARRAY_ELEMS(layouts)]; vector /= FF_ARRAY_ELEMS(layouts); + out_ch_layout = layouts[vector % FF_ARRAY_ELEMS(layouts)]; vector /= FF_ARRAY_ELEMS(layouts); + in_sample_fmt = formats[vector % FF_ARRAY_ELEMS(formats)]; vector /= FF_ARRAY_ELEMS(formats); + out_sample_fmt = formats[vector % FF_ARRAY_ELEMS(formats)]; vector /= FF_ARRAY_ELEMS(formats); + out_sample_rate = rates [vector % FF_ARRAY_ELEMS(rates )]; vector /= FF_ARRAY_ELEMS(rates); + av_assert0(!vector); + + if(specific_test == 0){ + if(out_sample_rate != in_sample_rate || in_ch_layout != out_ch_layout) + continue; + } + + in_ch_count= av_get_channel_layout_nb_channels(in_ch_layout); + out_ch_count= av_get_channel_layout_nb_channels(out_ch_layout); + av_get_channel_layout_string( in_layout_string, sizeof( in_layout_string), in_ch_count, in_ch_layout); + av_get_channel_layout_string(out_layout_string, sizeof(out_layout_string), out_ch_count, out_ch_layout); + fprintf(stderr, "TEST: %s->%s, rate:%5d->%5d, fmt:%s->%s\n", + in_layout_string, out_layout_string, + 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, out_sample_fmt, out_sample_rate, + in_ch_layout, in_sample_fmt, 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, out_sample_fmt, 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 , in_sample_fmt, SAMPLES); + setup_array(amid, array_mid, out_sample_fmt, 3*SAMPLES); + setup_array(aout, array_out, in_sample_fmt , SAMPLES); +#if 0 + for(ch=0; ch<in_ch_count; ch++){ + for(i=0; i<SAMPLES; i++) + set(ain, ch, i, in_ch_count, in_sample_fmt, sin(i*i*3/SAMPLES)); + } +#else + audiogen(ain, in_sample_fmt, in_ch_count, SAMPLES/6+1, SAMPLES); +#endif + mode = uint_rand(rand_seed) % 3; + if(mode==0 /*|| out_sample_rate == in_sample_rate*/) { + mid_count= swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, SAMPLES); + } else if(mode==1){ + mid_count= swr_convert(forw_ctx, amid, 0, (const uint8_t **)ain, SAMPLES); + mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); + } else { + int tmp_count; + mid_count= swr_convert(forw_ctx, amid, 0, (const uint8_t **)ain, 1); + av_assert0(mid_count==0); + shift(ain, 1, in_ch_count, in_sample_fmt); + mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); + shift(amid, mid_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; + mid_count+=swr_convert(forw_ctx, amid, 2, (const uint8_t **)ain, 2); + shift(amid, mid_count-tmp_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; + shift(ain, 2, in_ch_count, in_sample_fmt); + mid_count+=swr_convert(forw_ctx, amid, 1, (const uint8_t **)ain, SAMPLES-3); + shift(amid, mid_count-tmp_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; + shift(ain, -3, in_ch_count, in_sample_fmt); + mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); + shift(amid, -tmp_count, out_ch_count, out_sample_fmt); + } + out_count= swr_convert(backw_ctx,aout, SAMPLES, (const uint8_t **)amid, mid_count); + + for(ch=0; ch<in_ch_count; ch++){ + double sse, 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, in_sample_fmt); + 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)); + } + sse= sum_aa + sum_bb - 2*sum_ab; + if(sse < 0 && sse > -0.00001) sse=0; //fix rounding error + + fprintf(stderr, "[e:%f c:%f max:%f] len:%5d\n", sqrt(sse/out_count), sum_ab/(sqrt(sum_aa*sum_bb)), maxdiff, out_count); + } + + flush_i++; + flush_i%=21; + flush_count = swr_convert(backw_ctx,aout, flush_i, 0, 0); + shift(aout, flush_i, in_ch_count, in_sample_fmt); + flush_count+= swr_convert(backw_ctx,aout, SAMPLES-flush_i, 0, 0); + shift(aout, -flush_i, in_ch_count, in_sample_fmt); + if(flush_count){ + for(ch=0; ch<in_ch_count; ch++){ + double sse, 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, in_sample_fmt); + 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)); + } + sse= sum_aa + sum_bb - 2*sum_ab; + if(sse < 0 && sse > -0.00001) sse=0; //fix rounding error + + fprintf(stderr, "[e:%f c:%f max:%f] len:%5d F:%3d\n", sqrt(sse/flush_count), sum_ab/(sqrt(sum_aa*sum_bb)), maxdiff, flush_count, flush_i); + } + } + + + fprintf(stderr, "\n"); + } + + return 0; +} diff --git a/libswresample/swresample.c b/libswresample/swresample.c new file mode 100644 index 0000000000..5d02aafb49 --- /dev/null +++ b/libswresample/swresample.c @@ -0,0 +1,795 @@ +/* + * Copyright (C) 2011-2012 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" + +#include <float.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 + +#define ALIGN 32 + +//TODO split options array out? +#define OFFSET(x) offsetof(SwrContext,x) +#define PARAM AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption options[]={ +{"ich" , "Input Channel Count" , OFFSET( in.ch_count ), AV_OPT_TYPE_INT , {.i64=2 }, 0 , SWR_CH_MAX, PARAM}, +{"in_channel_count" , "Input Channel Count" , OFFSET( in.ch_count ), AV_OPT_TYPE_INT , {.i64=2 }, 0 , SWR_CH_MAX, PARAM}, +{"och" , "Output Channel Count" , OFFSET(out.ch_count ), AV_OPT_TYPE_INT , {.i64=2 }, 0 , SWR_CH_MAX, PARAM}, +{"out_channel_count" , "Output Channel Count" , OFFSET(out.ch_count ), AV_OPT_TYPE_INT , {.i64=2 }, 0 , SWR_CH_MAX, PARAM}, +{"uch" , "Used Channel Count" , OFFSET(used_ch_count ), AV_OPT_TYPE_INT , {.i64=0 }, 0 , SWR_CH_MAX, PARAM}, +{"used_channel_count" , "Used Channel Count" , OFFSET(used_ch_count ), AV_OPT_TYPE_INT , {.i64=0 }, 0 , SWR_CH_MAX, PARAM}, +{"isr" , "Input Sample Rate" , OFFSET( in_sample_rate), AV_OPT_TYPE_INT , {.i64=0 }, 0 , INT_MAX , PARAM}, +{"in_sample_rate" , "Input Sample Rate" , OFFSET( in_sample_rate), AV_OPT_TYPE_INT , {.i64=0 }, 0 , INT_MAX , PARAM}, +{"osr" , "Output Sample Rate" , OFFSET(out_sample_rate), AV_OPT_TYPE_INT , {.i64=0 }, 0 , INT_MAX , PARAM}, +{"out_sample_rate" , "Output Sample Rate" , OFFSET(out_sample_rate), AV_OPT_TYPE_INT , {.i64=0 }, 0 , INT_MAX , PARAM}, +{"isf" , "Input Sample Format" , OFFSET( in_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_NB-1+256, PARAM}, +{"in_sample_fmt" , "Input Sample Format" , OFFSET( in_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_NB-1+256, PARAM}, +{"osf" , "Output Sample Format" , OFFSET(out_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_NB-1+256, PARAM}, +{"out_sample_fmt" , "Output Sample Format" , OFFSET(out_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_NB-1+256, PARAM}, +{"tsf" , "Internal Sample Format" , OFFSET(int_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_FLTP, PARAM}, +{"internal_sample_fmt" , "Internal Sample Format" , OFFSET(int_sample_fmt ), AV_OPT_TYPE_INT , {.i64=AV_SAMPLE_FMT_NONE }, -1 , AV_SAMPLE_FMT_FLTP, PARAM}, +{"icl" , "Input Channel Layout" , OFFSET( in_ch_layout ), AV_OPT_TYPE_INT64, {.i64=0 }, 0 , INT64_MAX , PARAM, "channel_layout"}, +{"in_channel_layout" , "Input Channel Layout" , OFFSET( in_ch_layout ), AV_OPT_TYPE_INT64, {.i64=0 }, 0 , INT64_MAX , PARAM, "channel_layout"}, +{"ocl" , "Output Channel Layout" , OFFSET(out_ch_layout ), AV_OPT_TYPE_INT64, {.i64=0 }, 0 , INT64_MAX , PARAM, "channel_layout"}, +{"out_channel_layout" , "Output Channel Layout" , OFFSET(out_ch_layout ), AV_OPT_TYPE_INT64, {.i64=0 }, 0 , INT64_MAX , PARAM, "channel_layout"}, +{"clev" , "Center Mix Level" , OFFSET(clev ), AV_OPT_TYPE_FLOAT, {.dbl=C_30DB }, -32 , 32 , PARAM}, +{"center_mix_level" , "Center Mix Level" , OFFSET(clev ), AV_OPT_TYPE_FLOAT, {.dbl=C_30DB }, -32 , 32 , PARAM}, +{"slev" , "Sourround Mix Level" , OFFSET(slev ), AV_OPT_TYPE_FLOAT, {.dbl=C_30DB }, -32 , 32 , PARAM}, +{"surround_mix_level" , "Sourround Mix Level" , OFFSET(slev ), AV_OPT_TYPE_FLOAT, {.dbl=C_30DB }, -32 , 32 , PARAM}, +{"lfe_mix_level" , "LFE Mix Level" , OFFSET(lfe_mix_level ), AV_OPT_TYPE_FLOAT, {.dbl=0 }, -32 , 32 , PARAM}, +{"rmvol" , "Rematrix Volume" , OFFSET(rematrix_volume), AV_OPT_TYPE_FLOAT, {.dbl=1.0 }, -1000 , 1000 , PARAM}, +{"rematrix_volume" , "Rematrix Volume" , OFFSET(rematrix_volume), AV_OPT_TYPE_FLOAT, {.dbl=1.0 }, -1000 , 1000 , PARAM}, +{"flags" , NULL , OFFSET(flags ), AV_OPT_TYPE_FLAGS, {.i64=0 }, 0 , UINT_MAX , PARAM, "flags"}, +{"swr_flags" , NULL , OFFSET(flags ), AV_OPT_TYPE_FLAGS, {.i64=0 }, 0 , UINT_MAX , PARAM, "flags"}, +{"res" , "Force Resampling" , 0 , AV_OPT_TYPE_CONST, {.i64=SWR_FLAG_RESAMPLE }, INT_MIN, INT_MAX , PARAM, "flags"}, +{"dither_scale" , "Dither Scale" , OFFSET(dither_scale ), AV_OPT_TYPE_FLOAT, {.dbl=1 }, 0 , INT_MAX , PARAM}, +{"dither_method" , "Dither Method" , OFFSET(dither_method ), AV_OPT_TYPE_INT , {.i64=0 }, 0 , SWR_DITHER_NB-1, PARAM, "dither_method"}, +{"rectangular" , "Rectangular Dither" , 0 , AV_OPT_TYPE_CONST, {.i64=SWR_DITHER_RECTANGULAR}, INT_MIN, INT_MAX , PARAM, "dither_method"}, +{"triangular" , "Triangular Dither" , 0 , AV_OPT_TYPE_CONST, {.i64=SWR_DITHER_TRIANGULAR }, INT_MIN, INT_MAX , PARAM, "dither_method"}, +{"triangular_hp" , "Triangular Dither With High Pass" , 0 , AV_OPT_TYPE_CONST, {.i64=SWR_DITHER_TRIANGULAR_HIGHPASS }, INT_MIN, INT_MAX, PARAM, "dither_method"}, +{"filter_size" , "Resampling Filter Size" , OFFSET(filter_size) , AV_OPT_TYPE_INT , {.i64=16 }, 0 , INT_MAX , PARAM }, +{"phase_shift" , "Resampling Phase Shift" , OFFSET(phase_shift) , AV_OPT_TYPE_INT , {.i64=10 }, 0 , 30 , PARAM }, +{"linear_interp" , "Use Linear Interpolation" , OFFSET(linear_interp) , AV_OPT_TYPE_INT , {.i64=0 }, 0 , 1 , PARAM }, +{"cutoff" , "Cutoff Frequency Ratio" , OFFSET(cutoff) , AV_OPT_TYPE_DOUBLE,{.dbl=0.8 }, 0 , 1 , PARAM }, +{"min_comp" , "Minimum difference between timestamps and audio data (in seconds) below which no timestamp compensation of either kind is applied" + , OFFSET(min_compensation),AV_OPT_TYPE_FLOAT ,{.dbl=FLT_MAX }, 0 , FLT_MAX , PARAM }, +{"min_hard_comp" , "Minimum difference between timestamps and audio data (in seconds) to trigger padding/trimming the data." + , OFFSET(min_hard_compensation),AV_OPT_TYPE_FLOAT ,{.dbl=0.1 }, 0 , INT_MAX , PARAM }, +{"comp_duration" , "Duration (in seconds) over which data is stretched/squeezed to make it match the timestamps." + , OFFSET(soft_compensation_duration),AV_OPT_TYPE_FLOAT ,{.dbl=1 }, 0 , INT_MAX , PARAM }, +{"max_soft_comp" , "Maximum factor by which data is stretched/squeezed to make it match the timestamps." + , OFFSET(max_soft_compensation),AV_OPT_TYPE_FLOAT ,{.dbl=0 }, INT_MIN, INT_MAX , PARAM }, +{ "matrix_encoding" , "Matrixed Stereo Encoding" , OFFSET(matrix_encoding), AV_OPT_TYPE_INT ,{.i64 = AV_MATRIX_ENCODING_NONE}, AV_MATRIX_ENCODING_NONE, AV_MATRIX_ENCODING_NB-1, PARAM, "matrix_encoding" }, + { "none", "None", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MATRIX_ENCODING_NONE }, INT_MIN, INT_MAX, PARAM, "matrix_encoding" }, + { "dolby", "Dolby", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MATRIX_ENCODING_DOLBY }, INT_MIN, INT_MAX, PARAM, "matrix_encoding" }, + { "dplii", "Dolby Pro Logic II", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MATRIX_ENCODING_DPLII }, INT_MIN, INT_MAX, PARAM, "matrix_encoding" }, +{ "filter_type" , "Filter Type" , OFFSET(filter_type) , AV_OPT_TYPE_INT , { .i64 = SWR_FILTER_TYPE_KAISER }, SWR_FILTER_TYPE_CUBIC, SWR_FILTER_TYPE_KAISER, PARAM, "filter_type" }, + { "cubic" , "Cubic" , 0 , AV_OPT_TYPE_CONST, { .i64 = SWR_FILTER_TYPE_CUBIC }, INT_MIN, INT_MAX, PARAM, "filter_type" }, + { "blackman_nuttall", "Blackman Nuttall Windowed Sinc", 0 , AV_OPT_TYPE_CONST, { .i64 = SWR_FILTER_TYPE_BLACKMAN_NUTTALL }, INT_MIN, INT_MAX, PARAM, "filter_type" }, + { "kaiser" , "Kaiser Windowed Sinc" , 0 , AV_OPT_TYPE_CONST, { .i64 = SWR_FILTER_TYPE_KAISER }, INT_MIN, INT_MAX, PARAM, "filter_type" }, +{ "kaiser_beta" , "Kaiser Window Beta" ,OFFSET(kaiser_beta) , AV_OPT_TYPE_INT , {.i64=9 }, 2 , 16 , PARAM }, + +{0} +}; + +static const char* context_to_name(void* ptr) { + return "SWR"; +} + +static const AVClass av_class = { + .class_name = "SWResampler", + .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), + .category = AV_CLASS_CATEGORY_SWRESAMPLER, +}; + +unsigned swresample_version(void) +{ + av_assert0(LIBSWRESAMPLE_VERSION_MICRO >= 100); + return LIBSWRESAMPLE_VERSION_INT; +} + +const char *swresample_configuration(void) +{ + return FFMPEG_CONFIGURATION; +} + +const char *swresample_license(void) +{ +#define LICENSE_PREFIX "libswresample license: " + return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; +} + +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; +} + +const AVClass *swr_get_class(void) +{ + return &av_class; +} + +av_cold 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_NONE, 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 set_audiodata_fmt(AudioData *a, enum AVSampleFormat fmt){ + a->fmt = fmt; + a->bps = av_get_bytes_per_sample(fmt); + a->planar= av_sample_fmt_is_planar(fmt); +} + +static void free_temp(AudioData *a){ + av_free(a->data); + memset(a, 0, sizeof(*a)); +} + +av_cold 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); + free_temp(&s->dither); + 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); + swri_rematrix_free(s); + } + + av_freep(ss); +} + +av_cold 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); + free_temp(&s->dither); + memset(s->in.ch, 0, sizeof(s->in.ch)); + memset(s->out.ch, 0, sizeof(s->out.ch)); + swri_audio_convert_free(&s-> in_convert); + swri_audio_convert_free(&s->out_convert); + swri_audio_convert_free(&s->full_convert); + swri_rematrix_free(s); + + s->flushed = 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_NONE){ + if(av_get_planar_sample_fmt(s->in_sample_fmt) <= AV_SAMPLE_FMT_S16P){ + s->int_sample_fmt= AV_SAMPLE_FMT_S16P; + }else if(av_get_planar_sample_fmt(s->in_sample_fmt) <= AV_SAMPLE_FMT_FLTP){ + s->int_sample_fmt= AV_SAMPLE_FMT_FLTP; + }else{ + av_log(s, AV_LOG_DEBUG, "Using double precision mode\n"); + s->int_sample_fmt= AV_SAMPLE_FMT_DBLP; + } + } + + if( s->int_sample_fmt != AV_SAMPLE_FMT_S16P + &&s->int_sample_fmt != AV_SAMPLE_FMT_S32P + &&s->int_sample_fmt != AV_SAMPLE_FMT_FLTP + &&s->int_sample_fmt != AV_SAMPLE_FMT_DBLP){ + av_log(s, AV_LOG_ERROR, "Requested sample format %s is not supported internally, S16/S32/FLT/DBL is supported\n", av_get_sample_fmt_name(s->int_sample_fmt)); + return AVERROR(EINVAL); + } + + set_audiodata_fmt(&s-> in, s-> in_sample_fmt); + set_audiodata_fmt(&s->out, s->out_sample_fmt); + + 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, s->filter_size, s->phase_shift, s->linear_interp, s->cutoff, s->int_sample_fmt, s->filter_type, s->kaiser_beta); + }else + swri_resample_free(&s->resample); + if( s->int_sample_fmt != AV_SAMPLE_FMT_S16P + && s->int_sample_fmt != AV_SAMPLE_FMT_S32P + && s->int_sample_fmt != AV_SAMPLE_FMT_FLTP + && s->int_sample_fmt != AV_SAMPLE_FMT_DBLP + && s->resample){ + av_log(s, AV_LOG_ERROR, "Resampling only supported with internal s16/s32/flt/dbl\n"); + 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 || + s->rematrix_custom; + +#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); + + if(!s-> in.ch_count){ + av_assert0(!s->in_ch_layout); + av_log(s, AV_LOG_ERROR, "Input channel count and layout are unset\n"); + return -1; + } + + if ((!s->out_ch_layout || !s->in_ch_layout) && s->used_ch_count != s->out.ch_count && !s->rematrix_custom) { + av_log(s, AV_LOG_ERROR, "Rematrix is needed but there is not enough information to do it\n"); + return -1; + } + +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_buffer= s->in; + + if(!s->resample && !s->rematrix && !s->channel_map && !s->dither_method){ + 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; + + if(s->channel_map){ + s->postin.ch_count= + s->midbuf.ch_count= s->used_ch_count; + if(s->resample) + s->in_buffer.ch_count= s->used_ch_count; + } + if(!s->resample_first){ + s->midbuf.ch_count= s->out.ch_count; + if(s->resample) + s->in_buffer.ch_count = s->out.ch_count; + } + + set_audiodata_fmt(&s->postin, s->int_sample_fmt); + set_audiodata_fmt(&s->midbuf, s->int_sample_fmt); + set_audiodata_fmt(&s->preout, s->int_sample_fmt); + + if(s->resample){ + set_audiodata_fmt(&s->in_buffer, s->int_sample_fmt); + } + + s->dither = s->preout; + + if(s->rematrix || s->dither_method) + return swri_rematrix_init(s); + + return 0; +} + +static int realloc_audio(AudioData *a, int count){ + int i, countb; + AudioData old; + + if(count < 0 || count > INT_MAX/2/a->bps/a->ch_count) + return AVERROR(EINVAL); + + if(a->count >= count) + return 0; + + count*=2; + + countb= FFALIGN(count*a->bps, ALIGN); + old= *a; + + av_assert0(a->bps); + av_assert0(a->ch_count); + + a->data= av_mallocz(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); + } + if(!a->planar) memcpy(a->ch[0], old.ch[0], a->count*a->ch_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(!in_arg){ + memset(out->ch, 0, sizeof(out->ch)); + }else 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; + } +} + +static void reversefill_audiodata(AudioData *out, uint8_t *in_arg [SWR_CH_MAX]){ + int i; + if(out->planar){ + for(i=0; i<out->ch_count; i++) + in_arg[i]= out->ch[i]; + }else{ + in_arg[0]= out->ch[0]; + } +} + +/** + * + * out may be equal in. + */ +static void buf_set(AudioData *out, AudioData *in, int count){ + int ch; + if(in->planar){ + for(ch=0; ch<out->ch_count; ch++) + out->ch[ch]= in->ch[ch] + count*out->bps; + }else{ + for(ch=out->ch_count-1; ch>=0; ch--) + out->ch[ch]= in->ch[0] + (ch + 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; + + av_assert1(s->in_buffer.ch_count == in_param->ch_count); + av_assert1(s->in_buffer.planar == in_param->planar); + av_assert1(s->in_buffer.fmt == in_param->fmt); + + 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; +} + +static int swr_convert_internal(struct SwrContext *s, AudioData *out, int out_count, + AudioData *in , int in_count){ + AudioData *postin, *midbuf, *preout; + int ret/*, in_max*/; + AudioData preout_tmp, midbuf_tmp; + + 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 && !s->channel_map) + 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 the 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 && out_count){ + if(s->dither_method){ + int ch; + int dither_count= FFMAX(out_count, 1<<16); + av_assert0(preout != in); + + if((ret=realloc_audio(&s->dither, dither_count))<0) + return ret; + if(ret) + for(ch=0; ch<s->dither.ch_count; ch++) + swri_get_dither(s, s->dither.ch[ch], s->dither.count, 12345678913579<<ch, s->out_sample_fmt, s->int_sample_fmt); + av_assert0(s->dither.ch_count == preout->ch_count); + + if(s->dither_pos + out_count > s->dither.count) + s->dither_pos = 0; + + for(ch=0; ch<preout->ch_count; ch++) + s->mix_2_1_f(preout->ch[ch], preout->ch[ch], s->dither.ch[ch] + s->dither.bps * s->dither_pos, s->native_one, 0, 0, out_count); + + s->dither_pos += out_count; + } +//FIXME packed doesnt need more than 1 chan here! + swri_audio_convert(s->out_convert, out, preout, out_count); + } + return out_count; +} + +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 * in= &s->in; + AudioData *out= &s->out; + + if(s->drop_output > 0){ + int ret; + AudioData tmp = s->out; + uint8_t *tmp_arg[SWR_CH_MAX]; + tmp.count = 0; + tmp.data = NULL; + if((ret=realloc_audio(&tmp, s->drop_output))<0) + return ret; + + reversefill_audiodata(&tmp, tmp_arg); + s->drop_output *= -1; //FIXME find a less hackish solution + ret = swr_convert(s, tmp_arg, -s->drop_output, in_arg, in_count); //FIXME optimize but this is as good as never called so maybe it doesnt matter + s->drop_output *= -1; + if(ret>0) + s->drop_output -= ret; + + av_freep(&tmp.data); + if(s->drop_output || !out_arg) + return 0; + in_count = 0; + } + + if(!in_arg){ + if(s->in_buffer_count){ + if (s->resample && !s->flushed) { + 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; + s->flushed = 1; + } + }else{ + return 0; + } + }else + fill_audiodata(in , (void*)in_arg); + + fill_audiodata(out, out_arg); + + if(s->resample){ + int ret = swr_convert_internal(s, out, out_count, in, in_count); + if(ret>0 && !s->drop_output) + s->outpts += ret * (int64_t)s->in_sample_rate; + return ret; + }else{ + AudioData tmp= *in; + int ret2=0; + int ret, size; + size = FFMIN(out_count, s->in_buffer_count); + if(size){ + buf_set(&tmp, &s->in_buffer, s->in_buffer_index); + ret= swr_convert_internal(s, out, size, &tmp, size); + if(ret<0) + return ret; + ret2= ret; + s->in_buffer_count -= ret; + s->in_buffer_index += ret; + buf_set(out, out, ret); + out_count -= ret; + if(!s->in_buffer_count) + s->in_buffer_index = 0; + } + + if(in_count){ + size= s->in_buffer_index + s->in_buffer_count + in_count - out_count; + + if(in_count > out_count) { //FIXME move after swr_convert_internal + if( size > s->in_buffer.count + && s->in_buffer_count + in_count - out_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(out_count){ + size = FFMIN(in_count, out_count); + ret= swr_convert_internal(s, out, size, in, size); + if(ret<0) + return ret; + buf_set(in, in, ret); + in_count -= ret; + ret2 += ret; + } + if(in_count){ + buf_set(&tmp, &s->in_buffer, s->in_buffer_index + s->in_buffer_count); + copy(&tmp, in, in_count); + s->in_buffer_count += in_count; + } + } + if(ret2>0 && !s->drop_output) + s->outpts += ret2 * (int64_t)s->in_sample_rate; + return ret2; + } +} + +int swr_drop_output(struct SwrContext *s, int count){ + s->drop_output += count; + + if(s->drop_output <= 0) + return 0; + + av_log(s, AV_LOG_VERBOSE, "discarding %d audio samples\n", count); + return swr_convert(s, NULL, s->drop_output, NULL, 0); +} + +int swr_inject_silence(struct SwrContext *s, int count){ + int ret, i; + AudioData silence = s->in; + uint8_t *tmp_arg[SWR_CH_MAX]; + + if(count <= 0) + return 0; + + silence.count = 0; + silence.data = NULL; + if((ret=realloc_audio(&silence, count))<0) + return ret; + + if(silence.planar) for(i=0; i<silence.ch_count; i++) { + memset(silence.ch[i], silence.bps==1 ? 0x80 : 0, count*silence.bps); + } else + memset(silence.ch[0], silence.bps==1 ? 0x80 : 0, count*silence.bps*silence.ch_count); + + reversefill_audiodata(&silence, tmp_arg); + av_log(s, AV_LOG_VERBOSE, "adding %d audio samples of silence\n", count); + ret = swr_convert(s, NULL, 0, (const uint8_t**)tmp_arg, count); + av_freep(&silence.data); + return ret; +} + +int64_t swr_next_pts(struct SwrContext *s, int64_t pts){ + if(pts == INT64_MIN) + return s->outpts; + if(s->min_compensation >= FLT_MAX) { + return (s->outpts = pts - swr_get_delay(s, s->in_sample_rate * (int64_t)s->out_sample_rate)); + } else { + int64_t delta = pts - swr_get_delay(s, s->in_sample_rate * (int64_t)s->out_sample_rate) - s->outpts; + double fdelta = delta /(double)(s->in_sample_rate * (int64_t)s->out_sample_rate); + + if(fabs(fdelta) > s->min_compensation) { + if(!s->outpts || fabs(fdelta) > s->min_hard_compensation){ + int ret; + if(delta > 0) ret = swr_inject_silence(s, delta / s->out_sample_rate); + else ret = swr_drop_output (s, -delta / s-> in_sample_rate); + if(ret<0){ + av_log(s, AV_LOG_ERROR, "Failed to compensate for timestamp delta of %f\n", fdelta); + } + } else if(s->soft_compensation_duration && s->max_soft_compensation) { + int duration = s->out_sample_rate * s->soft_compensation_duration; + double max_soft_compensation = s->max_soft_compensation / (s->max_soft_compensation < 0 ? -s->in_sample_rate : 1); + int comp = av_clipf(fdelta, -max_soft_compensation, max_soft_compensation) * duration ; + av_log(s, AV_LOG_VERBOSE, "compensating audio timestamp drift:%f compensation:%d in:%d\n", fdelta, comp, duration); + swr_set_compensation(s, comp, duration); + } + } + + return s->outpts; + } +} diff --git a/libswresample/swresample.h b/libswresample/swresample.h new file mode 100644 index 0000000000..ac43cd25c4 --- /dev/null +++ b/libswresample/swresample.h @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2011-2012 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" + +#include "libswresample/version.h" + +#if LIBSWRESAMPLE_VERSION_MAJOR < 1 +#define SWR_CH_MAX 32 ///< Maximum number of channels +#endif + +#define SWR_FLAG_RESAMPLE 1 ///< Force resampling even if equal sample rate +//TODO use int resample ? +//long term TODO can we enable this dynamically? + +enum SwrDitherType { + SWR_DITHER_NONE = 0, + SWR_DITHER_RECTANGULAR, + SWR_DITHER_TRIANGULAR, + SWR_DITHER_TRIANGULAR_HIGHPASS, + SWR_DITHER_NB, ///< not part of API/ABI +}; + +/** Resampling Filter Types */ +enum SwrFilterType { + SWR_FILTER_TYPE_CUBIC, /**< Cubic */ + SWR_FILTER_TYPE_BLACKMAN_NUTTALL, /**< Blackman Nuttall Windowed Sinc */ + SWR_FILTER_TYPE_KAISER, /**< Kaiser Windowed Sinc */ +}; + +typedef struct SwrContext SwrContext; + +/** + * Get the AVClass for swrContext. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *swr_get_class(void); + +/** + * 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. + * + * If more input is provided than output space then the input will be buffered. + * You can avoid this buffering by providing more output space than input. + * Convertion will run directly without copying whenever possible. + * + * @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, negative value on error + */ +int swr_convert(struct SwrContext *s, uint8_t **out, int out_count, + const uint8_t **in , int in_count); + +/** + * Convert the next timestamp from input to output + * timestampe are in 1/(in_sample_rate * out_sample_rate) units. + * + * @note There are 2 slightly differently behaving modes. + * First is when automatic timestamp compensation is not used, (min_compensation >= FLT_MAX) + * in this case timestamps will be passed through with delays compensated + * Second is when automatic timestamp compensation is used, (min_compensation < FLT_MAX) + * in this case the output timestamps will match output sample numbers + * + * @param pts timstamp for the next input sample, INT64_MIN if unknown + * @returns the output timestamp for the next output sample + */ +int64_t swr_next_pts(struct SwrContext *s, int64_t pts); + +/** + * Activate resampling compensation. + */ +int swr_set_compensation(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); + +/** + * Set a customized remix matrix. + * + * @param s allocated Swr context, not yet initialized + * @param matrix remix coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o + * @param stride offset between lines of the matrix + * @return AVERROR error code in case of failure. + */ +int swr_set_matrix(struct SwrContext *s, const double *matrix, int stride); + +/** + * Drops the specified number of output samples. + */ +int swr_drop_output(struct SwrContext *s, int count); + +/** + * Injects the specified number of silence samples. + */ +int swr_inject_silence(struct SwrContext *s, int count); + +/** + * Gets the delay the next input sample will experience relative to the next output sample. + * + * Swresample can buffer data if more input has been provided than available + * output space, also converting between sample rates needs a delay. + * This function returns the sum of all such delays. + * + * @param s swr context + * @param base timebase in which the returned delay will be + * if its set to 1 the returned delay is in seconds + * if its set to 1000 the returned delay is in milli seconds + * if its set to the input sample rate then the returned delay is in input samples + * if its set to the output sample rate then the returned delay is in output samples + * an exact rounding free delay can be found by using LCM(in_sample_rate, out_sample_rate) + * @returns the delay in 1/base units. + */ +int64_t swr_get_delay(struct SwrContext *s, int64_t base); + +/** + * Return the LIBSWRESAMPLE_VERSION_INT constant. + */ +unsigned swresample_version(void); + +/** + * Return the swr build-time configuration. + */ +const char *swresample_configuration(void); + +/** + * Return the swr license. + */ +const char *swresample_license(void); + +#endif diff --git a/libswresample/swresample_internal.h b/libswresample/swresample_internal.h new file mode 100644 index 0000000000..ab17602c64 --- /dev/null +++ b/libswresample/swresample_internal.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2011-2012 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" +#include "libavutil/audioconvert.h" + +#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ + +typedef void (mix_1_1_func_type)(void *out, const void *in, void *coeffp, int index, int len); +typedef void (mix_2_1_func_type)(void *out, const void *in1, const void *in2, void *coeffp, int index1, int index2, int len); + +typedef void (mix_any_func_type)(uint8_t **out, const uint8_t **in1, void *coeffp, int len); + +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 + enum AVSampleFormat fmt; ///< sample format +} AudioData; + +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_FLTP or AV_SAMPLE_FMT_S16P) + 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 lfe_mix_level; ///< LFE mixing level + float rematrix_volume; ///< rematrixing volume coefficient + enum AVMatrixEncoding matrix_encoding; /**< matrixed stereo encoding */ + 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) + enum SwrDitherType dither_method; + int dither_pos; + float dither_scale; + 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 */ + enum SwrFilterType filter_type; /**< resampling filter type */ + int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */ + + float min_compensation; ///< minimum below which no compensation will happen + float min_hard_compensation; ///< minimum below which no silence inject / sample drop will happen + float soft_compensation_duration; ///< duration over which soft compensation is applied + float max_soft_compensation; ///< maximum soft compensation in seconds over soft_compensation_duration + + 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) + int rematrix_custom; ///< flag to indicate that a custom matrix has been defined + + 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) + AudioData dither; ///< noise used for dithering + 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 + int flushed; ///< 1 if data is to be flushed and no further input is expected + int64_t outpts; ///< output PTS + int drop_output; ///< number of output samples to drop + + 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 + uint8_t *native_matrix; + uint8_t *native_one; + uint8_t *native_simd_matrix; + 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 + mix_1_1_func_type *mix_1_1_f; + mix_1_1_func_type *mix_1_1_simd; + + mix_2_1_func_type *mix_2_1_f; + mix_2_1_func_type *mix_2_1_simd; + + mix_any_func_type *mix_any_f; + + /* TODO: callbacks for ASM optimizations */ +}; + +struct ResampleContext *swri_resample_init(struct ResampleContext *, int out_rate, int in_rate, int filter_size, int phase_shift, int linear, double cutoff, enum AVSampleFormat, enum SwrFilterType, int kaiser_beta); +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_int16(struct ResampleContext *c, int16_t *dst, const int16_t *src, int *consumed, int src_size, int dst_size, int update_ctx); +int swri_resample_int32(struct ResampleContext *c, int32_t *dst, const int32_t *src, int *consumed, int src_size, int dst_size, int update_ctx); +int swri_resample_float(struct ResampleContext *c, float *dst, const float *src, int *consumed, int src_size, int dst_size, int update_ctx); +int swri_resample_double(struct ResampleContext *c,double *dst, const double *src, int *consumed, int src_size, int dst_size, int update_ctx); + +int swri_rematrix_init(SwrContext *s); +void swri_rematrix_free(SwrContext *s); +int swri_rematrix(SwrContext *s, AudioData *out, AudioData *in, int len, int mustcopy); +void swri_rematrix_init_x86(struct SwrContext *s); + +void swri_get_dither(SwrContext *s, void *dst, int len, unsigned seed, enum AVSampleFormat out_fmt, enum AVSampleFormat in_fmt); + +void swri_audio_convert_init_arm(struct AudioConvert *ac, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels); +void swri_audio_convert_init_x86(struct AudioConvert *ac, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels); +#endif diff --git a/libswresample/version.h b/libswresample/version.h new file mode 100644 index 0000000000..2c7b932818 --- /dev/null +++ b/libswresample/version.h @@ -0,0 +1,45 @@ +/* + * Version macros. + * + * 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_VERSION_H +#define SWR_VERSION_H + +/** + * @file + * Libswresample version macros + */ + +#include "libavutil/avutil.h" + +#define LIBSWRESAMPLE_VERSION_MAJOR 0 +#define LIBSWRESAMPLE_VERSION_MINOR 16 +#define LIBSWRESAMPLE_VERSION_MICRO 100 + +#define LIBSWRESAMPLE_VERSION_INT AV_VERSION_INT(LIBSWRESAMPLE_VERSION_MAJOR, \ + LIBSWRESAMPLE_VERSION_MINOR, \ + LIBSWRESAMPLE_VERSION_MICRO) +#define LIBSWRESAMPLE_VERSION AV_VERSION(LIBSWRESAMPLE_VERSION_MAJOR, \ + LIBSWRESAMPLE_VERSION_MINOR, \ + LIBSWRESAMPLE_VERSION_MICRO) +#define LIBSWRESAMPLE_BUILD LIBSWRESAMPLE_VERSION_INT + +#define LIBSWRESAMPLE_IDENT "SwR" AV_STRINGIFY(LIBSWRESAMPLE_VERSION) + +#endif /* SWR_VERSION_H */ diff --git a/libswresample/x86/Makefile b/libswresample/x86/Makefile new file mode 100644 index 0000000000..e8feede6b6 --- /dev/null +++ b/libswresample/x86/Makefile @@ -0,0 +1,3 @@ +YASM-OBJS += x86/swresample_x86.o\ + x86/audio_convert.o\ + x86/rematrix.o\ diff --git a/libswresample/x86/audio_convert.asm b/libswresample/x86/audio_convert.asm new file mode 100644 index 0000000000..29701a594c --- /dev/null +++ b/libswresample/x86/audio_convert.asm @@ -0,0 +1,462 @@ +;****************************************************************************** +;* Copyright (c) 2012 Michael Niedermayer +;* +;* 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 +;****************************************************************************** + +%include "libavutil/x86/x86inc.asm" +%include "libavutil/x86/x86util.asm" + +SECTION_RODATA +align 32 +flt2pm31: times 8 dd 4.6566129e-10 +flt2p31 : times 8 dd 2147483648.0 +flt2p15 : times 8 dd 32768.0 + +word_unpack_shuf : db 0, 1, 4, 5, 8, 9,12,13, 2, 3, 6, 7,10,11,14,15 + +SECTION .text + + +;to, from, a/u, log2_outsize, log_intsize, const +%macro PACK_2CH 5-7 +cglobal pack_2ch_%2_to_%1_%3, 3, 4, 6, dst, src, len, src2 + mov src2q , [srcq+gprsize] + mov srcq , [srcq] + mov dstq , [dstq] +%ifidn %3, a + test dstq, mmsize-1 + jne pack_2ch_%2_to_%1_u_int %+ SUFFIX + test srcq, mmsize-1 + jne pack_2ch_%2_to_%1_u_int %+ SUFFIX + test src2q, mmsize-1 + jne pack_2ch_%2_to_%1_u_int %+ SUFFIX +%else +pack_2ch_%2_to_%1_u_int %+ SUFFIX +%endif + lea srcq , [srcq + (1<<%5)*lenq] + lea src2q, [src2q + (1<<%5)*lenq] + lea dstq , [dstq + (2<<%4)*lenq] + neg lenq + %7 m0,m1,m2,m3,m4,m5 +.next: +%if %4 >= %5 + mov%3 m0, [ srcq +(1<<%5)*lenq] + mova m1, m0 + mov%3 m2, [ src2q+(1<<%5)*lenq] +%if %5 == 1 + punpcklwd m0, m2 + punpckhwd m1, m2 +%else + punpckldq m0, m2 + punpckhdq m1, m2 +%endif + %6 m0,m1,m2,m3,m4,m5 +%else + mov%3 m0, [ srcq +(1<<%5)*lenq] + mov%3 m1, [mmsize + srcq +(1<<%5)*lenq] + mov%3 m2, [ src2q+(1<<%5)*lenq] + mov%3 m3, [mmsize + src2q+(1<<%5)*lenq] + %6 m0,m1,m2,m3,m4,m5 + mova m2, m0 + punpcklwd m0, m1 + punpckhwd m2, m1 + SWAP 1,2 +%endif + mov%3 [ dstq+(2<<%4)*lenq], m0 + mov%3 [ mmsize + dstq+(2<<%4)*lenq], m1 +%if %4 > %5 + mov%3 [2*mmsize + dstq+(2<<%4)*lenq], m2 + mov%3 [3*mmsize + dstq+(2<<%4)*lenq], m3 + add lenq, 4*mmsize/(2<<%4) +%else + add lenq, 2*mmsize/(2<<%4) +%endif + jl .next + REP_RET +%endmacro + +%macro UNPACK_2CH 5-7 +cglobal unpack_2ch_%2_to_%1_%3, 3, 4, 7, dst, src, len, dst2 + mov dst2q , [dstq+gprsize] + mov srcq , [srcq] + mov dstq , [dstq] +%ifidn %3, a + test dstq, mmsize-1 + jne unpack_2ch_%2_to_%1_u_int %+ SUFFIX + test srcq, mmsize-1 + jne unpack_2ch_%2_to_%1_u_int %+ SUFFIX + test dst2q, mmsize-1 + jne unpack_2ch_%2_to_%1_u_int %+ SUFFIX +%else +unpack_2ch_%2_to_%1_u_int %+ SUFFIX +%endif + lea srcq , [srcq + (2<<%5)*lenq] + lea dstq , [dstq + (1<<%4)*lenq] + lea dst2q, [dst2q + (1<<%4)*lenq] + neg lenq + %7 m0,m1,m2,m3,m4,m5 + mova m6, [word_unpack_shuf] +.next: + mov%3 m0, [ srcq +(2<<%5)*lenq] + mov%3 m2, [ mmsize + srcq +(2<<%5)*lenq] +%if %5 == 1 +%ifidn SUFFIX, _ssse3 + pshufb m0, m6 + mova m1, m0 + pshufb m2, m6 + punpcklqdq m0,m2 + punpckhqdq m1,m2 +%else + mova m1, m0 + punpcklwd m0,m2 + punpckhwd m1,m2 + + mova m2, m0 + punpcklwd m0,m1 + punpckhwd m2,m1 + + mova m1, m0 + punpcklwd m0,m2 + punpckhwd m1,m2 +%endif +%else + mova m1, m0 + shufps m0, m2, 10001000b + shufps m1, m2, 11011101b +%endif +%if %4 < %5 + mov%3 m2, [2*mmsize + srcq +(2<<%5)*lenq] + mova m3, m2 + mov%3 m4, [3*mmsize + srcq +(2<<%5)*lenq] + shufps m2, m4, 10001000b + shufps m3, m4, 11011101b + SWAP 1,2 +%endif + %6 m0,m1,m2,m3,m4,m5 + mov%3 [ dstq+(1<<%4)*lenq], m0 +%if %4 > %5 + mov%3 [ dst2q+(1<<%4)*lenq], m2 + mov%3 [ mmsize + dstq+(1<<%4)*lenq], m1 + mov%3 [ mmsize + dst2q+(1<<%4)*lenq], m3 + add lenq, 2*mmsize/(1<<%4) +%else + mov%3 [ dst2q+(1<<%4)*lenq], m1 + add lenq, mmsize/(1<<%4) +%endif + jl .next + REP_RET +%endmacro + +%macro CONV 5-7 +cglobal %2_to_%1_%3, 3, 3, 6, dst, src, len + mov srcq , [srcq] + mov dstq , [dstq] +%ifidn %3, a + test dstq, mmsize-1 + jne %2_to_%1_u_int %+ SUFFIX + test srcq, mmsize-1 + jne %2_to_%1_u_int %+ SUFFIX +%else +%2_to_%1_u_int %+ SUFFIX +%endif + lea srcq , [srcq + (1<<%5)*lenq] + lea dstq , [dstq + (1<<%4)*lenq] + neg lenq + %7 m0,m1,m2,m3,m4,m5 +.next: + mov%3 m0, [ srcq +(1<<%5)*lenq] + mov%3 m1, [ mmsize + srcq +(1<<%5)*lenq] +%if %4 < %5 + mov%3 m2, [2*mmsize + srcq +(1<<%5)*lenq] + mov%3 m3, [3*mmsize + srcq +(1<<%5)*lenq] +%endif + %6 m0,m1,m2,m3,m4,m5 + mov%3 [ dstq+(1<<%4)*lenq], m0 + mov%3 [ mmsize + dstq+(1<<%4)*lenq], m1 +%if %4 > %5 + mov%3 [2*mmsize + dstq+(1<<%4)*lenq], m2 + mov%3 [3*mmsize + dstq+(1<<%4)*lenq], m3 + add lenq, 4*mmsize/(1<<%4) +%else + add lenq, 2*mmsize/(1<<%4) +%endif + jl .next + REP_RET +%endmacro + +%macro PACK_6CH 5-7 +cglobal pack_6ch_%2_to_%1_%3, 2,8,7, dst, src, src1, src2, src3, src4, src5, len +%if ARCH_X86_64 + mov lend, r2d +%else + %define lend dword r2m +%endif + mov src1q, [srcq+1*gprsize] + mov src2q, [srcq+2*gprsize] + mov src3q, [srcq+3*gprsize] + mov src4q, [srcq+4*gprsize] + mov src5q, [srcq+5*gprsize] + mov srcq, [srcq] + mov dstq, [dstq] +%ifidn %3, a + test dstq, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX + test srcq, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX + test src2q, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX + test src3q, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX + test src4q, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX + test src5q, mmsize-1 + jne pack_6ch_%2_to_%1_u_int %+ SUFFIX +%else +pack_6ch_%2_to_%1_u_int %+ SUFFIX +%endif + sub src1q, srcq + sub src2q, srcq + sub src3q, srcq + sub src4q, srcq + sub src5q, srcq +.loop: + mov%3 m0, [srcq ] + mov%3 m1, [srcq+src1q] + mov%3 m2, [srcq+src2q] + mov%3 m3, [srcq+src3q] + mov%3 m4, [srcq+src4q] + mov%3 m5, [srcq+src5q] + %7 x,x,x,x,m7,x +%if cpuflag(sse4) + SBUTTERFLYPS 0, 1, 6 + SBUTTERFLYPS 2, 3, 6 + SBUTTERFLYPS 4, 5, 6 + + blendps m6, m4, m0, 1100b + movlhps m0, m2 + movhlps m4, m2 + blendps m2, m5, m1, 1100b + movlhps m1, m3 + movhlps m5, m3 + + %6 m0,m6,x,x,m7,m3 + %6 m4,m1,x,x,m7,m3 + %6 m2,m5,x,x,m7,m3 + + mov %+ %3 %+ ps [dstq ], m0 + mov %+ %3 %+ ps [dstq+16], m6 + mov %+ %3 %+ ps [dstq+32], m4 + mov %+ %3 %+ ps [dstq+48], m1 + mov %+ %3 %+ ps [dstq+64], m2 + mov %+ %3 %+ ps [dstq+80], m5 +%else ; mmx + SBUTTERFLY dq, 0, 1, 6 + SBUTTERFLY dq, 2, 3, 6 + SBUTTERFLY dq, 4, 5, 6 + + movq [dstq ], m0 + movq [dstq+ 8], m2 + movq [dstq+16], m4 + movq [dstq+24], m1 + movq [dstq+32], m3 + movq [dstq+40], m5 +%endif + add srcq, mmsize + add dstq, mmsize*6 + sub lend, mmsize/4 + jg .loop +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + +%macro INT16_TO_INT32_N 6 + pxor m2, m2 + pxor m3, m3 + punpcklwd m2, m1 + punpckhwd m3, m1 + SWAP 4,0 + pxor m0, m0 + pxor m1, m1 + punpcklwd m0, m4 + punpckhwd m1, m4 +%endmacro + +%macro INT32_TO_INT16_N 6 + psrad m0, 16 + psrad m1, 16 + psrad m2, 16 + psrad m3, 16 + packssdw m0, m1 + packssdw m2, m3 + SWAP 1,2 +%endmacro + +%macro INT32_TO_FLOAT_INIT 6 + mova %5, [flt2pm31] +%endmacro +%macro INT32_TO_FLOAT_N 6 + cvtdq2ps %1, %1 + cvtdq2ps %2, %2 + mulps %1, %1, %5 + mulps %2, %2, %5 +%endmacro + +%macro FLOAT_TO_INT32_INIT 6 + mova %5, [flt2p31] +%endmacro +%macro FLOAT_TO_INT32_N 6 + mulps %1, %5 + mulps %2, %5 + cvtps2dq %6, %1 + cmpnltps %1, %5 + paddd %1, %6 + cvtps2dq %6, %2 + cmpnltps %2, %5 + paddd %2, %6 +%endmacro + +%macro INT16_TO_FLOAT_INIT 6 + mova m5, [flt2pm31] +%endmacro +%macro INT16_TO_FLOAT_N 6 + INT16_TO_INT32_N %1,%2,%3,%4,%5,%6 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + mulps m0, m0, m5 + mulps m1, m1, m5 + mulps m2, m2, m5 + mulps m3, m3, m5 +%endmacro + +%macro FLOAT_TO_INT16_INIT 6 + mova m5, [flt2p15] +%endmacro +%macro FLOAT_TO_INT16_N 6 + mulps m0, m5 + mulps m1, m5 + mulps m2, m5 + mulps m3, m5 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + packssdw m0, m1 + cvtps2dq m1, m2 + cvtps2dq m3, m3 + packssdw m1, m3 +%endmacro + +%macro NOP_N 0-6 +%endmacro + +INIT_MMX mmx +CONV int32, int16, u, 2, 1, INT16_TO_INT32_N, NOP_N +CONV int32, int16, a, 2, 1, INT16_TO_INT32_N, NOP_N +CONV int16, int32, u, 1, 2, INT32_TO_INT16_N, NOP_N +CONV int16, int32, a, 1, 2, INT32_TO_INT16_N, NOP_N + +PACK_6CH float, float, u, 2, 2, NOP_N, NOP_N +PACK_6CH float, float, a, 2, 2, NOP_N, NOP_N + +INIT_XMM sse2 +CONV int32, int16, u, 2, 1, INT16_TO_INT32_N, NOP_N +CONV int32, int16, a, 2, 1, INT16_TO_INT32_N, NOP_N +CONV int16, int32, u, 1, 2, INT32_TO_INT16_N, NOP_N +CONV int16, int32, a, 1, 2, INT32_TO_INT16_N, NOP_N + +PACK_2CH int16, int16, u, 1, 1, NOP_N, NOP_N +PACK_2CH int16, int16, a, 1, 1, NOP_N, NOP_N +PACK_2CH int32, int32, u, 2, 2, NOP_N, NOP_N +PACK_2CH int32, int32, a, 2, 2, NOP_N, NOP_N +PACK_2CH int32, int16, u, 2, 1, INT16_TO_INT32_N, NOP_N +PACK_2CH int32, int16, a, 2, 1, INT16_TO_INT32_N, NOP_N +PACK_2CH int16, int32, u, 1, 2, INT32_TO_INT16_N, NOP_N +PACK_2CH int16, int32, a, 1, 2, INT32_TO_INT16_N, NOP_N + +UNPACK_2CH int16, int16, u, 1, 1, NOP_N, NOP_N +UNPACK_2CH int16, int16, a, 1, 1, NOP_N, NOP_N +UNPACK_2CH int32, int32, u, 2, 2, NOP_N, NOP_N +UNPACK_2CH int32, int32, a, 2, 2, NOP_N, NOP_N +UNPACK_2CH int32, int16, u, 2, 1, INT16_TO_INT32_N, NOP_N +UNPACK_2CH int32, int16, a, 2, 1, INT16_TO_INT32_N, NOP_N +UNPACK_2CH int16, int32, u, 1, 2, INT32_TO_INT16_N, NOP_N +UNPACK_2CH int16, int32, a, 1, 2, INT32_TO_INT16_N, NOP_N + +CONV float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +CONV float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +CONV int32, float, u, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +CONV int32, float, a, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +CONV float, int16, u, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +CONV float, int16, a, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +CONV int16, float, u, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT +CONV int16, float, a, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT + +PACK_2CH float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_2CH float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_2CH int32, float, u, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +PACK_2CH int32, float, a, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +PACK_2CH float, int16, u, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +PACK_2CH float, int16, a, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +PACK_2CH int16, float, u, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT +PACK_2CH int16, float, a, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT + +UNPACK_2CH float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +UNPACK_2CH float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +UNPACK_2CH int32, float, u, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +UNPACK_2CH int32, float, a, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +UNPACK_2CH float, int16, u, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +UNPACK_2CH float, int16, a, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +UNPACK_2CH int16, float, u, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT +UNPACK_2CH int16, float, a, 1, 2, FLOAT_TO_INT16_N, FLOAT_TO_INT16_INIT + + +INIT_XMM ssse3 +UNPACK_2CH int16, int16, u, 1, 1, NOP_N, NOP_N +UNPACK_2CH int16, int16, a, 1, 1, NOP_N, NOP_N +UNPACK_2CH int32, int16, u, 2, 1, INT16_TO_INT32_N, NOP_N +UNPACK_2CH int32, int16, a, 2, 1, INT16_TO_INT32_N, NOP_N +UNPACK_2CH float, int16, u, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT +UNPACK_2CH float, int16, a, 2, 1, INT16_TO_FLOAT_N, INT16_TO_FLOAT_INIT + +INIT_XMM sse4 +PACK_6CH float, float, u, 2, 2, NOP_N, NOP_N +PACK_6CH float, float, a, 2, 2, NOP_N, NOP_N + +PACK_6CH float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_6CH float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_6CH int32, float, u, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +PACK_6CH int32, float, a, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT + +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +PACK_6CH float, float, u, 2, 2, NOP_N, NOP_N +PACK_6CH float, float, a, 2, 2, NOP_N, NOP_N + +PACK_6CH float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_6CH float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +PACK_6CH int32, float, u, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT +PACK_6CH int32, float, a, 2, 2, FLOAT_TO_INT32_N, FLOAT_TO_INT32_INIT + +INIT_YMM avx +CONV float, int32, u, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +CONV float, int32, a, 2, 2, INT32_TO_FLOAT_N, INT32_TO_FLOAT_INIT +%endif diff --git a/libswresample/x86/rematrix.asm b/libswresample/x86/rematrix.asm new file mode 100644 index 0000000000..d63545ff5e --- /dev/null +++ b/libswresample/x86/rematrix.asm @@ -0,0 +1,252 @@ +;****************************************************************************** +;* Copyright (c) 2012 Michael Niedermayer +;* +;* 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 +;****************************************************************************** + +%include "libavutil/x86/x86inc.asm" +%include "libavutil/x86/x86util.asm" + + +SECTION_RODATA +align 32 +dw1: times 8 dd 1 +w1 : times 16 dw 1 + +SECTION .text + +%macro MIX2_FLT 1 +cglobal mix_2_1_%1_float, 7, 7, 6, out, in1, in2, coeffp, index1, index2, len +%ifidn %1, a + test in1q, mmsize-1 + jne mix_2_1_float_u_int %+ SUFFIX + test in2q, mmsize-1 + jne mix_2_1_float_u_int %+ SUFFIX + test outq, mmsize-1 + jne mix_2_1_float_u_int %+ SUFFIX +%else +mix_2_1_float_u_int %+ SUFFIX +%endif + VBROADCASTSS m4, [coeffpq + 4*index1q] + VBROADCASTSS m5, [coeffpq + 4*index2q] + shl lend , 2 + add in1q , lenq + add in2q , lenq + add outq , lenq + neg lenq +.next: +%ifidn %1, a + mulps m0, m4, [in1q + lenq ] + mulps m1, m5, [in2q + lenq ] + mulps m2, m4, [in1q + lenq + mmsize] + mulps m3, m5, [in2q + lenq + mmsize] +%else + movu m0, [in1q + lenq ] + movu m1, [in2q + lenq ] + movu m2, [in1q + lenq + mmsize] + movu m3, [in2q + lenq + mmsize] + mulps m0, m0, m4 + mulps m1, m1, m5 + mulps m2, m2, m4 + mulps m3, m3, m5 +%endif + addps m0, m0, m1 + addps m2, m2, m3 + mov%1 [outq + lenq ], m0 + mov%1 [outq + lenq + mmsize], m2 + add lenq, mmsize*2 + jl .next + REP_RET +%endmacro + +%macro MIX1_FLT 1 +cglobal mix_1_1_%1_float, 5, 5, 3, out, in, coeffp, index, len +%ifidn %1, a + test inq, mmsize-1 + jne mix_1_1_float_u_int %+ SUFFIX + test outq, mmsize-1 + jne mix_1_1_float_u_int %+ SUFFIX +%else +mix_1_1_float_u_int %+ SUFFIX +%endif + VBROADCASTSS m2, [coeffpq + 4*indexq] + shl lenq , 2 + add inq , lenq + add outq , lenq + neg lenq +.next: +%ifidn %1, a + mulps m0, m2, [inq + lenq ] + mulps m1, m2, [inq + lenq + mmsize] +%else + movu m0, [inq + lenq ] + movu m1, [inq + lenq + mmsize] + mulps m0, m0, m2 + mulps m1, m1, m2 +%endif + mov%1 [outq + lenq ], m0 + mov%1 [outq + lenq + mmsize], m1 + add lenq, mmsize*2 + jl .next + REP_RET +%endmacro + +%macro MIX1_INT16 1 +cglobal mix_1_1_%1_int16, 5, 5, 6, out, in, coeffp, index, len +%ifidn %1, a + test inq, mmsize-1 + jne mix_1_1_int16_u_int %+ SUFFIX + test outq, mmsize-1 + jne mix_1_1_int16_u_int %+ SUFFIX +%else +mix_1_1_int16_u_int %+ SUFFIX +%endif + movd m4, [coeffpq + 4*indexq] + SPLATW m5, m4 + psllq m4, 32 + psrlq m4, 48 + mova m0, [w1] + psllw m0, m4 + psrlw m0, 1 + punpcklwd m5, m0 + add lenq , lenq + add inq , lenq + add outq , lenq + neg lenq +.next: + mov%1 m0, [inq + lenq ] + mov%1 m2, [inq + lenq + mmsize] + mova m1, m0 + mova m3, m2 + punpcklwd m0, [w1] + punpckhwd m1, [w1] + punpcklwd m2, [w1] + punpckhwd m3, [w1] + pmaddwd m0, m5 + pmaddwd m1, m5 + pmaddwd m2, m5 + pmaddwd m3, m5 + psrad m0, m4 + psrad m1, m4 + psrad m2, m4 + psrad m3, m4 + packssdw m0, m1 + packssdw m2, m3 + mov%1 [outq + lenq ], m0 + mov%1 [outq + lenq + mmsize], m2 + add lenq, mmsize*2 + jl .next +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + +%macro MIX2_INT16 1 +cglobal mix_2_1_%1_int16, 7, 7, 8, out, in1, in2, coeffp, index1, index2, len +%ifidn %1, a + test in1q, mmsize-1 + jne mix_2_1_int16_u_int %+ SUFFIX + test in2q, mmsize-1 + jne mix_2_1_int16_u_int %+ SUFFIX + test outq, mmsize-1 + jne mix_2_1_int16_u_int %+ SUFFIX +%else +mix_2_1_int16_u_int %+ SUFFIX +%endif + movd m4, [coeffpq + 4*index1q] + movd m6, [coeffpq + 4*index2q] + SPLATW m5, m4 + SPLATW m6, m6 + psllq m4, 32 + psrlq m4, 48 + mova m7, [dw1] + pslld m7, m4 + psrld m7, 1 + punpcklwd m5, m6 + add lend , lend + add in1q , lenq + add in2q , lenq + add outq , lenq + neg lenq +.next: + mov%1 m0, [in1q + lenq ] + mov%1 m2, [in2q + lenq ] + mova m1, m0 + punpcklwd m0, m2 + punpckhwd m1, m2 + + mov%1 m2, [in1q + lenq + mmsize] + mov%1 m6, [in2q + lenq + mmsize] + mova m3, m2 + punpcklwd m2, m6 + punpckhwd m3, m6 + + pmaddwd m0, m5 + pmaddwd m1, m5 + pmaddwd m2, m5 + pmaddwd m3, m5 + paddd m0, m7 + paddd m1, m7 + paddd m2, m7 + paddd m3, m7 + psrad m0, m4 + psrad m1, m4 + psrad m2, m4 + psrad m3, m4 + packssdw m0, m1 + packssdw m2, m3 + mov%1 [outq + lenq ], m0 + mov%1 [outq + lenq + mmsize], m2 + add lenq, mmsize*2 + jl .next +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + + +INIT_MMX mmx +MIX1_INT16 u +MIX1_INT16 a +MIX2_INT16 u +MIX2_INT16 a + +INIT_XMM sse +MIX2_FLT u +MIX2_FLT a +MIX1_FLT u +MIX1_FLT a + +INIT_XMM sse2 +MIX1_INT16 u +MIX1_INT16 a +MIX2_INT16 u +MIX2_INT16 a + +%if HAVE_AVX_EXTERNAL +INIT_YMM avx +MIX2_FLT u +MIX2_FLT a +MIX1_FLT u +MIX1_FLT a +%endif diff --git a/libswresample/x86/resample_mmx.h b/libswresample/x86/resample_mmx.h new file mode 100644 index 0000000000..d96fd5a9d2 --- /dev/null +++ b/libswresample/x86/resample_mmx.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012 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 + */ + +#include "libavutil/x86/asm.h" +#include "libavutil/cpu.h" +#include "libswresample/swresample_internal.h" + +int swri_resample_int16_mmx2 (struct ResampleContext *c, int16_t *dst, const int16_t *src, int *consumed, int src_size, int dst_size, int update_ctx); +int swri_resample_int16_ssse3(struct ResampleContext *c, int16_t *dst, const int16_t *src, int *consumed, int src_size, int dst_size, int update_ctx); + +DECLARE_ALIGNED(16, const uint64_t, ff_resample_int16_rounder)[2] = { 0x0000000000004000ULL, 0x0000000000000000ULL}; + +#define COMMON_CORE_INT16_MMX2 \ + x86_reg len= -2*c->filter_length;\ +__asm__ volatile(\ + "movq "MANGLE(ff_resample_int16_rounder)", %%mm0 \n\t"\ + "1: \n\t"\ + "movq (%1, %0), %%mm1 \n\t"\ + "pmaddwd (%2, %0), %%mm1 \n\t"\ + "paddd %%mm1, %%mm0 \n\t"\ + "add $8, %0 \n\t"\ + " js 1b \n\t"\ + "pshufw $0x0E, %%mm0, %%mm1 \n\t"\ + "paddd %%mm1, %%mm0 \n\t"\ + "psrad $15, %%mm0 \n\t"\ + "packssdw %%mm0, %%mm0 \n\t"\ + "movd %%mm0, (%3) \n\t"\ + : "+r" (len)\ + : "r" (((uint8_t*)(src+sample_index))-len),\ + "r" (((uint8_t*)filter)-len),\ + "r" (dst+dst_index)\ +); + +#define COMMON_CORE_INT16_SSSE3 \ + x86_reg len= -2*c->filter_length;\ +__asm__ volatile(\ + "movdqa "MANGLE(ff_resample_int16_rounder)", %%xmm0 \n\t"\ + "1: \n\t"\ + "movdqu (%1, %0), %%xmm1 \n\t"\ + "pmaddwd (%2, %0), %%xmm1 \n\t"\ + "paddd %%xmm1, %%xmm0 \n\t"\ + "add $16, %0 \n\t"\ + " js 1b \n\t"\ + "phaddd %%xmm0, %%xmm0 \n\t"\ + "phaddd %%xmm0, %%xmm0 \n\t"\ + "psrad $15, %%xmm0 \n\t"\ + "packssdw %%xmm0, %%xmm0 \n\t"\ + "movd %%xmm0, (%3) \n\t"\ + : "+r" (len)\ + : "r" (((uint8_t*)(src+sample_index))-len),\ + "r" (((uint8_t*)filter)-len),\ + "r" (dst+dst_index)\ +); diff --git a/libswresample/x86/swresample_x86.c b/libswresample/x86/swresample_x86.c new file mode 100644 index 0000000000..e18f0c57ba --- /dev/null +++ b/libswresample/x86/swresample_x86.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2012 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 "libswresample/swresample_internal.h" +#include "libswresample/audioconvert.h" + +#define PROTO(pre, in, out, cap) void ff ## pre ## _ ##in## _to_ ##out## _a_ ##cap(uint8_t **dst, const uint8_t **src, int len); +#define PROTO2(pre, out, cap) PROTO(pre, int16, out, cap) PROTO(pre, int32, out, cap) PROTO(pre, float, out, cap) +#define PROTO3(pre, cap) PROTO2(pre, int16, cap) PROTO2(pre, int32, cap) PROTO2(pre, float, cap) +#define PROTO4(pre) PROTO3(pre, mmx) PROTO3(pre, sse) PROTO3(pre, sse2) PROTO3(pre, ssse3) PROTO3(pre, sse4) PROTO3(pre, avx) +PROTO4() +PROTO4(_pack_2ch) +PROTO4(_pack_6ch) +PROTO4(_unpack_2ch) + +av_cold void swri_audio_convert_init_x86(struct AudioConvert *ac, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels){ + int mm_flags = av_get_cpu_flags(); + + ac->simd_f= NULL; + +//FIXME add memcpy case + +#define MULTI_CAPS_FUNC(flag, cap) \ + if (mm_flags & flag) {\ + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S16 || out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_S16P)\ + ac->simd_f = ff_int16_to_int32_a_ ## cap;\ + if( out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_S32 || out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_S32P)\ + ac->simd_f = ff_int32_to_int16_a_ ## cap;\ + } + +MULTI_CAPS_FUNC(AV_CPU_FLAG_MMX, mmx) +MULTI_CAPS_FUNC(AV_CPU_FLAG_SSE2, sse2) + + if(mm_flags & AV_CPU_FLAG_MMX) { + if(channels == 6) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_FLTP || out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_6ch_float_to_float_a_mmx; + } + } + + if(mm_flags & AV_CPU_FLAG_SSE2) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S32 || out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_int32_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S16 || out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S16P) + ac->simd_f = ff_int16_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_FLT || out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_float_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_FLT || out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_float_to_int16_a_sse2; + + if(channels == 2) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_FLTP || out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_2ch_int32_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_S16P) + ac->simd_f = ff_pack_2ch_int16_to_int16_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S16P) + ac->simd_f = ff_pack_2ch_int16_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_2ch_int32_to_int16_a_sse2; + + if( out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_FLT || out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_S32) + ac->simd_f = ff_unpack_2ch_int32_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_int16_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_S32) + ac->simd_f = ff_unpack_2ch_int32_to_int16_a_sse2; + + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_2ch_int32_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_pack_2ch_float_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S16P) + ac->simd_f = ff_pack_2ch_int16_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16 && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_pack_2ch_float_to_int16_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S32) + ac->simd_f = ff_unpack_2ch_int32_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_FLT) + ac->simd_f = ff_unpack_2ch_float_to_int32_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_float_a_sse2; + if( out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_FLT) + ac->simd_f = ff_unpack_2ch_float_to_int16_a_sse2; + } + } + if(mm_flags & AV_CPU_FLAG_SSSE3) { + if(channels == 2) { + if( out_fmt == AV_SAMPLE_FMT_S16P && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_int16_a_ssse3; + if( out_fmt == AV_SAMPLE_FMT_S32P && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_int32_a_ssse3; + if( out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S16) + ac->simd_f = ff_unpack_2ch_int16_to_float_a_ssse3; + } + } + if(mm_flags & AV_CPU_FLAG_SSE4) { + if(channels == 6) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_FLTP || out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_6ch_float_to_float_a_sse4; + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_6ch_int32_to_float_a_sse4; + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_pack_6ch_float_to_int32_a_sse4; + } + } + if(HAVE_AVX_EXTERNAL && mm_flags & AV_CPU_FLAG_AVX) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S32 || out_fmt == AV_SAMPLE_FMT_FLTP && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_int32_to_float_a_avx; + if(channels == 6) { + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_FLTP || out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_6ch_float_to_float_a_avx; + if( out_fmt == AV_SAMPLE_FMT_FLT && in_fmt == AV_SAMPLE_FMT_S32P) + ac->simd_f = ff_pack_6ch_int32_to_float_a_avx; + if( out_fmt == AV_SAMPLE_FMT_S32 && in_fmt == AV_SAMPLE_FMT_FLTP) + ac->simd_f = ff_pack_6ch_float_to_int32_a_avx; + } + } +} + +#define D(type, simd) \ +mix_1_1_func_type ff_mix_1_1_a_## type ## _ ## simd;\ +mix_2_1_func_type ff_mix_2_1_a_## type ## _ ## simd; + +D(float, sse) +D(float, avx) +D(int16, mmx) +D(int16, sse2) + + +av_cold void swri_rematrix_init_x86(struct SwrContext *s){ + int mm_flags = av_get_cpu_flags(); + int nb_in = av_get_channel_layout_nb_channels(s->in_ch_layout); + int nb_out = av_get_channel_layout_nb_channels(s->out_ch_layout); + int num = nb_in * nb_out; + int i,j; + + s->mix_1_1_simd = NULL; + s->mix_2_1_simd = NULL; + + if (s->midbuf.fmt == AV_SAMPLE_FMT_S16P){ + if(mm_flags & AV_CPU_FLAG_MMX) { + s->mix_1_1_simd = ff_mix_1_1_a_int16_mmx; + s->mix_2_1_simd = ff_mix_2_1_a_int16_mmx; + } + if(mm_flags & AV_CPU_FLAG_SSE2) { + s->mix_1_1_simd = ff_mix_1_1_a_int16_sse2; + s->mix_2_1_simd = ff_mix_2_1_a_int16_sse2; + } + s->native_simd_matrix = av_mallocz(2 * num * sizeof(int16_t)); + for(i=0; i<nb_out; i++){ + int sh = 0; + for(j=0; j<nb_in; j++) + sh = FFMAX(sh, FFABS(((int*)s->native_matrix)[i * nb_in + j])); + sh = FFMAX(av_log2(sh) - 14, 0); + for(j=0; j<nb_in; j++) { + ((int16_t*)s->native_simd_matrix)[2*(i * nb_in + j)+1] = 15 - sh; + ((int16_t*)s->native_simd_matrix)[2*(i * nb_in + j)] = + ((((int*)s->native_matrix)[i * nb_in + j]) + (1<<sh>>1)) >> sh; + } + } + } else if(s->midbuf.fmt == AV_SAMPLE_FMT_FLTP){ + if(mm_flags & AV_CPU_FLAG_SSE) { + s->mix_1_1_simd = ff_mix_1_1_a_float_sse; + s->mix_2_1_simd = ff_mix_2_1_a_float_sse; + } + if(HAVE_AVX_EXTERNAL && mm_flags & AV_CPU_FLAG_AVX) { + s->mix_1_1_simd = ff_mix_1_1_a_float_avx; + s->mix_2_1_simd = ff_mix_2_1_a_float_avx; + } + s->native_simd_matrix = av_mallocz(num * sizeof(float)); + memcpy(s->native_simd_matrix, s->native_matrix, num * sizeof(float)); + } +} |