diff options
author | FlyingRat <flyingrat@outlook.com> | 2013-04-07 16:36:04 +0200 |
---|---|---|
committer | FlyingRat <flyingrat@outlook.com> | 2013-04-07 16:36:04 +0200 |
commit | 0e63a815aa6af63a21848e04b683d3f506dd41b1 (patch) | |
tree | 002f61d8a5b1d294d99fd4ba5b6982d76a612f0c /lib/ffmpeg/libavresample | |
parent | 71862137c5337fc678681a23bfbc65f4db7a7b2f (diff) |
[FFmpeg] version bump to n1.2 (rev e820e3a) - lib/ffmpeg
This commit now contains the original patches sub directory:
patches - Org dir that contains applied xbmc custom patches.
patches/README-patches - New README file with info about xbmc patches.
patches/obsolete-patches - New dir with obsolete xbmc patches.
Diffstat (limited to 'lib/ffmpeg/libavresample')
31 files changed, 7917 insertions, 0 deletions
diff --git a/lib/ffmpeg/libavresample/Makefile b/lib/ffmpeg/libavresample/Makefile new file mode 100644 index 0000000000..68052802ed --- /dev/null +++ b/lib/ffmpeg/libavresample/Makefile @@ -0,0 +1,16 @@ +NAME = avresample +FFLIBS = avutil + +HEADERS = avresample.h \ + version.h \ + +OBJS = audio_convert.o \ + audio_data.o \ + audio_mix.o \ + audio_mix_matrix.o \ + dither.o \ + options.o \ + resample.o \ + utils.o \ + +TESTPROGS = avresample diff --git a/lib/ffmpeg/libavresample/arm/Makefile b/lib/ffmpeg/libavresample/arm/Makefile new file mode 100644 index 0000000000..55683cbfae --- /dev/null +++ b/lib/ffmpeg/libavresample/arm/Makefile @@ -0,0 +1,2 @@ +OBJS += arm/audio_convert_init.o +NEON-OBJS += arm/audio_convert_neon.o diff --git a/lib/ffmpeg/libavresample/arm/audio_convert_init.c b/lib/ffmpeg/libavresample/arm/audio_convert_init.c new file mode 100644 index 0000000000..3d19a0e0e5 --- /dev/null +++ b/lib/ffmpeg/libavresample/arm/audio_convert_init.c @@ -0,0 +1,49 @@ +/* + * 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 <stdint.h> + +#include "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/arm/cpu.h" +#include "libavutil/samplefmt.h" +#include "libavresample/audio_convert.h" + +void ff_conv_flt_to_s16_neon(int16_t *dst, const float *src, int len); +void ff_conv_fltp_to_s16_neon(int16_t *dst, float *const *src, + int len, int channels); +void ff_conv_fltp_to_s16_2ch_neon(int16_t *dst, float *const *src, + int len, int channels); + +av_cold void ff_audio_convert_init_arm(AudioConvert *ac) +{ + int cpu_flags = av_get_cpu_flags(); + + if (have_neon(cpu_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT, + 0, 16, 8, "NEON", + ff_conv_flt_to_s16_neon); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 0, 16, 8, "NEON", + ff_conv_fltp_to_s16_neon); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 2, 16, 8, "NEON", + ff_conv_fltp_to_s16_2ch_neon); + } +} diff --git a/lib/ffmpeg/libavresample/arm/audio_convert_neon.S b/lib/ffmpeg/libavresample/arm/audio_convert_neon.S new file mode 100644 index 0000000000..98f77f058c --- /dev/null +++ b/lib/ffmpeg/libavresample/arm/audio_convert_neon.S @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2008 Mans Rullgard <mans@mansr.com> + * + * 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 "config.h" +#include "libavutil/arm/asm.S" + +function ff_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 ff_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 ff_conv_fltp_to_s16_neon, export=1 + cmp r3, #2 + itt lt + ldrlt r1, [r1] + blt ff_conv_flt_to_s16_neon + beq ff_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/lib/ffmpeg/libavresample/audio_convert.c b/lib/ffmpeg/libavresample/audio_convert.c new file mode 100644 index 0000000000..371617cc25 --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_convert.c @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at> + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> + +#include "config.h" +#include "libavutil/common.h" +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "audio_convert.h" +#include "audio_data.h" +#include "dither.h" + +enum ConvFuncType { + CONV_FUNC_TYPE_FLAT, + CONV_FUNC_TYPE_INTERLEAVE, + CONV_FUNC_TYPE_DEINTERLEAVE, +}; + +typedef void (conv_func_flat)(uint8_t *out, const uint8_t *in, int len); + +typedef void (conv_func_interleave)(uint8_t *out, uint8_t *const *in, + int len, int channels); + +typedef void (conv_func_deinterleave)(uint8_t **out, const uint8_t *in, int len, + int channels); + +struct AudioConvert { + AVAudioResampleContext *avr; + DitherContext *dc; + enum AVSampleFormat in_fmt; + enum AVSampleFormat out_fmt; + int apply_map; + int channels; + int planes; + int ptr_align; + int samples_align; + int has_optimized_func; + const char *func_descr; + const char *func_descr_generic; + enum ConvFuncType func_type; + conv_func_flat *conv_flat; + conv_func_flat *conv_flat_generic; + conv_func_interleave *conv_interleave; + conv_func_interleave *conv_interleave_generic; + conv_func_deinterleave *conv_deinterleave; + conv_func_deinterleave *conv_deinterleave_generic; +}; + +void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, int channels, + int ptr_align, int samples_align, + const char *descr, void *conv) +{ + int found = 0; + + switch (ac->func_type) { + case CONV_FUNC_TYPE_FLAT: + if (av_get_packed_sample_fmt(ac->in_fmt) == in_fmt && + av_get_packed_sample_fmt(ac->out_fmt) == out_fmt) { + ac->conv_flat = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_flat_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + case CONV_FUNC_TYPE_INTERLEAVE: + if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt && + (!channels || ac->channels == channels)) { + ac->conv_interleave = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_interleave_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + case CONV_FUNC_TYPE_DEINTERLEAVE: + if (ac->in_fmt == in_fmt && ac->out_fmt == out_fmt && + (!channels || ac->channels == channels)) { + ac->conv_deinterleave = conv; + ac->func_descr = descr; + ac->ptr_align = ptr_align; + ac->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + ac->conv_deinterleave_generic = conv; + ac->func_descr_generic = descr; + } else { + ac->has_optimized_func = 1; + } + found = 1; + } + break; + } + if (found) { + av_log(ac->avr, AV_LOG_DEBUG, "audio_convert: found function: %-4s " + "to %-4s (%s)\n", av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt), descr); + } +} + +#define CONV_FUNC_NAME(dst_fmt, src_fmt) conv_ ## src_fmt ## _to_ ## dst_fmt + +#define CONV_LOOP(otype, expr) \ + do { \ + *(otype *)po = expr; \ + pi += is; \ + po += os; \ + } while (po < end); \ + +#define CONV_FUNC_FLAT(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t *in, \ + int len) \ +{ \ + int is = sizeof(itype); \ + int os = sizeof(otype); \ + const uint8_t *pi = in; \ + uint8_t *po = out; \ + uint8_t *end = out + os * len; \ + CONV_LOOP(otype, expr) \ +} + +#define CONV_FUNC_INTERLEAVE(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t *out, const uint8_t **in, \ + int len, int channels) \ +{ \ + int ch; \ + int out_bps = sizeof(otype); \ + int is = sizeof(itype); \ + int os = channels * out_bps; \ + for (ch = 0; ch < channels; ch++) { \ + const uint8_t *pi = in[ch]; \ + uint8_t *po = out + ch * out_bps; \ + uint8_t *end = po + os * len; \ + CONV_LOOP(otype, expr) \ + } \ +} + +#define CONV_FUNC_DEINTERLEAVE(ofmt, otype, ifmt, itype, expr) \ +static void CONV_FUNC_NAME(ofmt, ifmt)(uint8_t **out, const uint8_t *in, \ + int len, int channels) \ +{ \ + int ch; \ + int in_bps = sizeof(itype); \ + int is = channels * in_bps; \ + int os = sizeof(otype); \ + for (ch = 0; ch < channels; ch++) { \ + const uint8_t *pi = in + ch * in_bps; \ + uint8_t *po = out[ch]; \ + uint8_t *end = po + os * len; \ + CONV_LOOP(otype, expr) \ + } \ +} + +#define CONV_FUNC_GROUP(ofmt, otype, ifmt, itype, expr) \ +CONV_FUNC_FLAT( ofmt, otype, ifmt, itype, expr) \ +CONV_FUNC_INTERLEAVE( ofmt, otype, ifmt ## P, itype, expr) \ +CONV_FUNC_DEINTERLEAVE(ofmt ## P, otype, ifmt, itype, expr) + +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_U8, uint8_t, *(const uint8_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) << 8) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) << 24) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0f / (1 << 7))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_U8, uint8_t, (*(const uint8_t *)pi - 0x80) * (1.0 / (1 << 7))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S16, int16_t, (*(const int16_t *)pi >> 8) + 0x80) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi << 16) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi * (1.0f / (1 << 15))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S16, int16_t, *(const int16_t *)pi * (1.0 / (1 << 15))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_S32, int32_t, (*(const int32_t *)pi >> 24) + 0x80) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi >> 16) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi * (1.0f / (1U << 31))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_S32, int32_t, *(const int32_t *)pi * (1.0 / (1U << 31))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_FLT, float, av_clip_uint8( lrintf(*(const float *)pi * (1 << 7)) + 0x80)) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_FLT, float, av_clip_int16( lrintf(*(const float *)pi * (1 << 15)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_FLT, float, av_clipl_int32(llrintf(*(const float *)pi * (1U << 31)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_FLT, float, *(const float *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_FLT, float, *(const float *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, uint8_t, AV_SAMPLE_FMT_DBL, double, av_clip_uint8( lrint(*(const double *)pi * (1 << 7)) + 0x80)) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, int16_t, AV_SAMPLE_FMT_DBL, double, av_clip_int16( lrint(*(const double *)pi * (1 << 15)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, int32_t, AV_SAMPLE_FMT_DBL, double, av_clipl_int32(llrint(*(const double *)pi * (1U << 31)))) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, float, AV_SAMPLE_FMT_DBL, double, *(const double *)pi) +CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, double, AV_SAMPLE_FMT_DBL, double, *(const double *)pi) + +#define SET_CONV_FUNC_GROUP(ofmt, ifmt) \ +ff_audio_convert_set_func(ac, ofmt, ifmt, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt, ifmt)); \ +ff_audio_convert_set_func(ac, ofmt ## P, ifmt, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt ## P, ifmt)); \ +ff_audio_convert_set_func(ac, ofmt, ifmt ## P, 0, 1, 1, "C", CONV_FUNC_NAME(ofmt, ifmt ## P)); + +static void set_generic_function(AudioConvert *ac) +{ + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_U8) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S16) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_S32) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_FLT) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_DBL) + SET_CONV_FUNC_GROUP(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBL) +} + +void ff_audio_convert_free(AudioConvert **ac) +{ + if (!*ac) + return; + ff_dither_free(&(*ac)->dc); + av_freep(ac); +} + +AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate, + int apply_map) +{ + AudioConvert *ac; + int in_planar, out_planar; + + ac = av_mallocz(sizeof(*ac)); + if (!ac) + return NULL; + + ac->avr = avr; + ac->out_fmt = out_fmt; + ac->in_fmt = in_fmt; + ac->channels = channels; + ac->apply_map = apply_map; + + if (avr->dither_method != AV_RESAMPLE_DITHER_NONE && + av_get_packed_sample_fmt(out_fmt) == AV_SAMPLE_FMT_S16 && + av_get_bytes_per_sample(in_fmt) > 2) { + ac->dc = ff_dither_alloc(avr, out_fmt, in_fmt, channels, sample_rate, + apply_map); + if (!ac->dc) { + av_free(ac); + return NULL; + } + return ac; + } + + in_planar = av_sample_fmt_is_planar(in_fmt); + out_planar = av_sample_fmt_is_planar(out_fmt); + + if (in_planar == out_planar) { + ac->func_type = CONV_FUNC_TYPE_FLAT; + ac->planes = in_planar ? ac->channels : 1; + } else if (in_planar) + ac->func_type = CONV_FUNC_TYPE_INTERLEAVE; + else + ac->func_type = CONV_FUNC_TYPE_DEINTERLEAVE; + + set_generic_function(ac); + + if (ARCH_ARM) + ff_audio_convert_init_arm(ac); + if (ARCH_X86) + ff_audio_convert_init_x86(ac); + + return ac; +} + +int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in) +{ + int use_generic = 1; + int len = in->nb_samples; + int p; + + if (ac->dc) { + /* dithered conversion */ + av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (dithered)\n", + len, av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt)); + + return ff_convert_dither(ac->dc, out, in); + } + + /* determine whether to use the optimized function based on pointer and + samples alignment in both the input and output */ + if (ac->has_optimized_func) { + int ptr_align = FFMIN(in->ptr_align, out->ptr_align); + int samples_align = FFMIN(in->samples_align, out->samples_align); + int aligned_len = FFALIGN(len, ac->samples_align); + if (!(ptr_align % ac->ptr_align) && samples_align >= aligned_len) { + len = aligned_len; + use_generic = 0; + } + } + av_dlog(ac->avr, "%d samples - audio_convert: %s to %s (%s)\n", len, + av_get_sample_fmt_name(ac->in_fmt), + av_get_sample_fmt_name(ac->out_fmt), + use_generic ? ac->func_descr_generic : ac->func_descr); + + if (ac->apply_map) { + ChannelMapInfo *map = &ac->avr->ch_map_info; + + if (!av_sample_fmt_is_planar(ac->out_fmt)) { + av_log(ac->avr, AV_LOG_ERROR, "cannot remap packed format during conversion\n"); + return AVERROR(EINVAL); + } + + if (map->do_remap) { + if (av_sample_fmt_is_planar(ac->in_fmt)) { + conv_func_flat *convert = use_generic ? ac->conv_flat_generic : + ac->conv_flat; + + for (p = 0; p < ac->planes; p++) + if (map->channel_map[p] >= 0) + convert(out->data[p], in->data[map->channel_map[p]], len); + } else { + uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; + conv_func_deinterleave *convert = use_generic ? + ac->conv_deinterleave_generic : + ac->conv_deinterleave; + + for (p = 0; p < ac->channels; p++) + data[map->input_map[p]] = out->data[p]; + + convert(data, in->data[0], len, ac->channels); + } + } + if (map->do_copy || map->do_zero) { + for (p = 0; p < ac->planes; p++) { + if (map->channel_copy[p]) + memcpy(out->data[p], out->data[map->channel_copy[p]], + len * out->stride); + else if (map->channel_zero[p]) + av_samples_set_silence(&out->data[p], 0, len, 1, ac->out_fmt); + } + } + } else { + switch (ac->func_type) { + case CONV_FUNC_TYPE_FLAT: { + if (!in->is_planar) + len *= in->channels; + if (use_generic) { + for (p = 0; p < ac->planes; p++) + ac->conv_flat_generic(out->data[p], in->data[p], len); + } else { + for (p = 0; p < ac->planes; p++) + ac->conv_flat(out->data[p], in->data[p], len); + } + break; + } + case CONV_FUNC_TYPE_INTERLEAVE: + if (use_generic) + ac->conv_interleave_generic(out->data[0], in->data, len, + ac->channels); + else + ac->conv_interleave(out->data[0], in->data, len, ac->channels); + break; + case CONV_FUNC_TYPE_DEINTERLEAVE: + if (use_generic) + ac->conv_deinterleave_generic(out->data, in->data[0], len, + ac->channels); + else + ac->conv_deinterleave(out->data, in->data[0], len, + ac->channels); + break; + } + } + + out->nb_samples = in->nb_samples; + return 0; +} diff --git a/lib/ffmpeg/libavresample/audio_convert.h b/lib/ffmpeg/libavresample/audio_convert.h new file mode 100644 index 0000000000..6a3089d4fb --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_convert.h @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_CONVERT_H +#define AVRESAMPLE_AUDIO_CONVERT_H + +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" + +/** + * Set conversion function if the parameters match. + * + * This compares the parameters of the conversion function to the parameters + * in the AudioConvert context. If the parameters do not match, no changes are + * made to the active functions. If the parameters do match and the alignment + * is not constrained, the function is set as the generic conversion function. + * If the parameters match and the alignment is constrained, the function is + * set as the optimized conversion function. + * + * @param ac AudioConvert context + * @param out_fmt output sample format + * @param in_fmt input sample format + * @param channels number of channels, or 0 for any number of channels + * @param ptr_align buffer pointer alignment, in bytes + * @param samples_align buffer size alignment, in samples + * @param descr function type description (e.g. "C" or "SSE") + * @param conv conversion function pointer + */ +void ff_audio_convert_set_func(AudioConvert *ac, enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, int channels, + int ptr_align, int samples_align, + const char *descr, void *conv); + +/** + * Allocate and initialize AudioConvert context for sample format conversion. + * + * @param avr AVAudioResampleContext + * @param out_fmt output sample format + * @param in_fmt input sample format + * @param channels number of channels + * @param sample_rate sample rate (used for dithering) + * @param apply_map apply channel map during conversion + * @return newly-allocated AudioConvert context + */ +AudioConvert *ff_audio_convert_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate, + int apply_map); + +/** + * Free AudioConvert. + * + * The AudioConvert must have been previously allocated with ff_audio_convert_alloc(). + * + * @param ac AudioConvert struct + */ +void ff_audio_convert_free(AudioConvert **ac); + +/** + * Convert audio data from one sample format to another. + * + * For each call, the alignment of the input and output AudioData buffers are + * examined to determine whether to use the generic or optimized conversion + * function (when available). + * + * The number of samples to convert is determined by in->nb_samples. The output + * buffer must be large enough to handle this many samples. out->nb_samples is + * set by this function before a successful return. + * + * @param ac AudioConvert context + * @param out output audio data + * @param in input audio data + * @return 0 on success, negative AVERROR code on failure + */ +int ff_audio_convert(AudioConvert *ac, AudioData *out, AudioData *in); + +/* arch-specific initialization functions */ + +void ff_audio_convert_init_arm(AudioConvert *ac); +void ff_audio_convert_init_x86(AudioConvert *ac); + +#endif /* AVRESAMPLE_AUDIO_CONVERT_H */ diff --git a/lib/ffmpeg/libavresample/audio_data.c b/lib/ffmpeg/libavresample/audio_data.c new file mode 100644 index 0000000000..c52f518e9a --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_data.c @@ -0,0 +1,372 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <string.h> + +#include "libavutil/mem.h" +#include "audio_data.h" + +static const AVClass audio_data_class = { + .class_name = "AudioData", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +/* + * Calculate alignment for data pointers. + */ +static void calc_ptr_alignment(AudioData *a) +{ + int p; + int min_align = 128; + + for (p = 0; p < a->planes; p++) { + int cur_align = 128; + while ((intptr_t)a->data[p] % cur_align) + cur_align >>= 1; + if (cur_align < min_align) + min_align = cur_align; + } + a->ptr_align = min_align; +} + +int ff_audio_data_set_channels(AudioData *a, int channels) +{ + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS || + channels > a->allocated_channels) + return AVERROR(EINVAL); + + a->channels = channels; + a->planes = a->is_planar ? channels : 1; + + calc_ptr_alignment(a); + + return 0; +} + +int ff_audio_data_init(AudioData *a, uint8_t **src, int plane_size, int channels, + int nb_samples, enum AVSampleFormat sample_fmt, + int read_only, const char *name) +{ + int p; + + memset(a, 0, sizeof(*a)); + a->class = &audio_data_class; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(a, AV_LOG_ERROR, "invalid channel count: %d\n", channels); + return AVERROR(EINVAL); + } + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_log(a, AV_LOG_ERROR, "invalid sample format\n"); + return AVERROR(EINVAL); + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + for (p = 0; p < (a->is_planar ? channels : 1); p++) { + if (!src[p]) { + av_log(a, AV_LOG_ERROR, "invalid NULL pointer for src[%d]\n", p); + return AVERROR(EINVAL); + } + a->data[p] = src[p]; + } + a->allocated_samples = nb_samples * !read_only; + a->nb_samples = nb_samples; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = read_only; + a->allow_realloc = 0; + a->name = name ? name : "{no name}"; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +AudioData *ff_audio_data_alloc(int channels, int nb_samples, + enum AVSampleFormat sample_fmt, const char *name) +{ + AudioData *a; + int ret; + + if (channels < 1 || channels > AVRESAMPLE_MAX_CHANNELS) + return NULL; + + a = av_mallocz(sizeof(*a)); + if (!a) + return NULL; + + a->sample_size = av_get_bytes_per_sample(sample_fmt); + if (!a->sample_size) { + av_free(a); + return NULL; + } + a->is_planar = av_sample_fmt_is_planar(sample_fmt); + a->planes = a->is_planar ? channels : 1; + a->stride = a->sample_size * (a->is_planar ? 1 : channels); + + a->class = &audio_data_class; + a->sample_fmt = sample_fmt; + a->channels = channels; + a->allocated_channels = channels; + a->read_only = 0; + a->allow_realloc = 1; + a->name = name ? name : "{no name}"; + + if (nb_samples > 0) { + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) { + av_free(a); + return NULL; + } + return a; + } else { + calc_ptr_alignment(a); + return a; + } +} + +int ff_audio_data_realloc(AudioData *a, int nb_samples) +{ + int ret, new_buf_size, plane_size, p; + + /* check if buffer is already large enough */ + if (a->allocated_samples >= nb_samples) + return 0; + + /* validate that the output is not read-only and realloc is allowed */ + if (a->read_only || !a->allow_realloc) + return AVERROR(EINVAL); + + new_buf_size = av_samples_get_buffer_size(&plane_size, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (new_buf_size < 0) + return new_buf_size; + + /* if there is already data in the buffer and the sample format is planar, + allocate a new buffer and copy the data, otherwise just realloc the + internal buffer and set new data pointers */ + if (a->nb_samples > 0 && a->is_planar) { + uint8_t *new_data[AVRESAMPLE_MAX_CHANNELS] = { NULL }; + + ret = av_samples_alloc(new_data, &plane_size, a->allocated_channels, + nb_samples, a->sample_fmt, 0); + if (ret < 0) + return ret; + + for (p = 0; p < a->planes; p++) + memcpy(new_data[p], a->data[p], a->nb_samples * a->stride); + + av_freep(&a->buffer); + memcpy(a->data, new_data, sizeof(new_data)); + a->buffer = a->data[0]; + } else { + av_freep(&a->buffer); + a->buffer = av_malloc(new_buf_size); + if (!a->buffer) + return AVERROR(ENOMEM); + ret = av_samples_fill_arrays(a->data, &plane_size, a->buffer, + a->allocated_channels, nb_samples, + a->sample_fmt, 0); + if (ret < 0) + return ret; + } + a->buffer_size = new_buf_size; + a->allocated_samples = nb_samples; + + calc_ptr_alignment(a); + a->samples_align = plane_size / a->stride; + + return 0; +} + +void ff_audio_data_free(AudioData **a) +{ + if (!*a) + return; + av_free((*a)->buffer); + av_freep(a); +} + +int ff_audio_data_copy(AudioData *dst, AudioData *src, ChannelMapInfo *map) +{ + int ret, p; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels < src->channels) + return AVERROR(EINVAL); + + if (map && !src->is_planar) { + av_log(src, AV_LOG_ERROR, "cannot remap packed format during copy\n"); + return AVERROR(EINVAL); + } + + /* if the input is empty, just empty the output */ + if (!src->nb_samples) { + dst->nb_samples = 0; + return 0; + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, src->nb_samples); + if (ret < 0) + return ret; + + /* copy data */ + if (map) { + if (map->do_remap) { + for (p = 0; p < src->planes; p++) { + if (map->channel_map[p] >= 0) + memcpy(dst->data[p], src->data[map->channel_map[p]], + src->nb_samples * src->stride); + } + } + if (map->do_copy || map->do_zero) { + for (p = 0; p < src->planes; p++) { + if (map->channel_copy[p]) + memcpy(dst->data[p], dst->data[map->channel_copy[p]], + src->nb_samples * src->stride); + else if (map->channel_zero[p]) + av_samples_set_silence(&dst->data[p], 0, src->nb_samples, + 1, dst->sample_fmt); + } + } + } else { + for (p = 0; p < src->planes; p++) + memcpy(dst->data[p], src->data[p], src->nb_samples * src->stride); + } + + dst->nb_samples = src->nb_samples; + + return 0; +} + +int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src, + int src_offset, int nb_samples) +{ + int ret, p, dst_offset2, dst_move_size; + + /* validate input/output compatibility */ + if (dst->sample_fmt != src->sample_fmt || dst->channels != src->channels) { + av_log(src, AV_LOG_ERROR, "sample format mismatch\n"); + return AVERROR(EINVAL); + } + + /* validate offsets are within the buffer bounds */ + if (dst_offset < 0 || dst_offset > dst->nb_samples || + src_offset < 0 || src_offset > src->nb_samples) { + av_log(src, AV_LOG_ERROR, "offset out-of-bounds: src=%d dst=%d\n", + src_offset, dst_offset); + return AVERROR(EINVAL); + } + + /* check offsets and sizes to see if we can just do nothing and return */ + if (nb_samples > src->nb_samples - src_offset) + nb_samples = src->nb_samples - src_offset; + if (nb_samples <= 0) + return 0; + + /* validate that the output is not read-only */ + if (dst->read_only) { + av_log(dst, AV_LOG_ERROR, "dst is read-only\n"); + return AVERROR(EINVAL); + } + + /* reallocate output if necessary */ + ret = ff_audio_data_realloc(dst, dst->nb_samples + nb_samples); + if (ret < 0) { + av_log(dst, AV_LOG_ERROR, "error reallocating dst\n"); + return ret; + } + + dst_offset2 = dst_offset + nb_samples; + dst_move_size = dst->nb_samples - dst_offset; + + for (p = 0; p < src->planes; p++) { + if (dst_move_size > 0) { + memmove(dst->data[p] + dst_offset2 * dst->stride, + dst->data[p] + dst_offset * dst->stride, + dst_move_size * dst->stride); + } + memcpy(dst->data[p] + dst_offset * dst->stride, + src->data[p] + src_offset * src->stride, + nb_samples * src->stride); + } + dst->nb_samples += nb_samples; + + return 0; +} + +void ff_audio_data_drain(AudioData *a, int nb_samples) +{ + if (a->nb_samples <= nb_samples) { + /* drain the whole buffer */ + a->nb_samples = 0; + } else { + int p; + int move_offset = a->stride * nb_samples; + int move_size = a->stride * (a->nb_samples - nb_samples); + + for (p = 0; p < a->planes; p++) + memmove(a->data[p], a->data[p] + move_offset, move_size); + + a->nb_samples -= nb_samples; + } +} + +int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset, + int nb_samples) +{ + uint8_t *offset_data[AVRESAMPLE_MAX_CHANNELS]; + int offset_size, p; + + if (offset >= a->nb_samples) + return 0; + offset_size = offset * a->stride; + for (p = 0; p < a->planes; p++) + offset_data[p] = a->data[p] + offset_size; + + return av_audio_fifo_write(af, (void **)offset_data, nb_samples); +} + +int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples) +{ + int ret; + + if (a->read_only) + return AVERROR(EINVAL); + + ret = ff_audio_data_realloc(a, nb_samples); + if (ret < 0) + return ret; + + ret = av_audio_fifo_read(af, (void **)a->data, nb_samples); + if (ret >= 0) + a->nb_samples = ret; + return ret; +} diff --git a/lib/ffmpeg/libavresample/audio_data.h b/lib/ffmpeg/libavresample/audio_data.h new file mode 100644 index 0000000000..97236bb5de --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_data.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_DATA_H +#define AVRESAMPLE_AUDIO_DATA_H + +#include <stdint.h> + +#include "libavutil/audio_fifo.h" +#include "libavutil/log.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" + +/** + * Audio buffer used for intermediate storage between conversion phases. + */ +struct AudioData { + const AVClass *class; /**< AVClass for logging */ + uint8_t *data[AVRESAMPLE_MAX_CHANNELS]; /**< data plane pointers */ + uint8_t *buffer; /**< data buffer */ + unsigned int buffer_size; /**< allocated buffer size */ + int allocated_samples; /**< number of samples the buffer can hold */ + int nb_samples; /**< current number of samples */ + enum AVSampleFormat sample_fmt; /**< sample format */ + int channels; /**< channel count */ + int allocated_channels; /**< allocated channel count */ + int is_planar; /**< sample format is planar */ + int planes; /**< number of data planes */ + int sample_size; /**< bytes per sample */ + int stride; /**< sample byte offset within a plane */ + int read_only; /**< data is read-only */ + int allow_realloc; /**< realloc is allowed */ + int ptr_align; /**< minimum data pointer alignment */ + int samples_align; /**< allocated samples alignment */ + const char *name; /**< name for debug logging */ +}; + +int ff_audio_data_set_channels(AudioData *a, int channels); + +/** + * Initialize AudioData using a given source. + * + * This does not allocate an internal buffer. It only sets the data pointers + * and audio parameters. + * + * @param a AudioData struct + * @param src source data pointers + * @param plane_size plane size, in bytes. + * This can be 0 if unknown, but that will lead to + * optimized functions not being used in many cases, + * which could slow down some conversions. + * @param channels channel count + * @param nb_samples number of samples in the source data + * @param sample_fmt sample format + * @param read_only indicates if buffer is read only or read/write + * @param name name for debug logging (can be NULL) + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_init(AudioData *a, uint8_t **src, int plane_size, int channels, + int nb_samples, enum AVSampleFormat sample_fmt, + int read_only, const char *name); + +/** + * Allocate AudioData. + * + * This allocates an internal buffer and sets audio parameters. + * + * @param channels channel count + * @param nb_samples number of samples to allocate space for + * @param sample_fmt sample format + * @param name name for debug logging (can be NULL) + * @return newly allocated AudioData struct, or NULL on error + */ +AudioData *ff_audio_data_alloc(int channels, int nb_samples, + enum AVSampleFormat sample_fmt, + const char *name); + +/** + * Reallocate AudioData. + * + * The AudioData must have been previously allocated with ff_audio_data_alloc(). + * + * @param a AudioData struct + * @param nb_samples number of samples to allocate space for + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_realloc(AudioData *a, int nb_samples); + +/** + * Free AudioData. + * + * The AudioData must have been previously allocated with ff_audio_data_alloc(). + * + * @param a AudioData struct + */ +void ff_audio_data_free(AudioData **a); + +/** + * Copy data from one AudioData to another. + * + * @param out output AudioData + * @param in input AudioData + * @param map channel map, NULL if not remapping + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_copy(AudioData *out, AudioData *in, ChannelMapInfo *map); + +/** + * Append data from one AudioData to the end of another. + * + * @param dst destination AudioData + * @param dst_offset offset, in samples, to start writing, relative to the + * start of dst + * @param src source AudioData + * @param src_offset offset, in samples, to start copying, relative to the + * start of the src + * @param nb_samples number of samples to copy + * @return 0 on success, negative AVERROR value on error + */ +int ff_audio_data_combine(AudioData *dst, int dst_offset, AudioData *src, + int src_offset, int nb_samples); + +/** + * Drain samples from the start of the AudioData. + * + * Remaining samples are shifted to the start of the AudioData. + * + * @param a AudioData struct + * @param nb_samples number of samples to drain + */ +void ff_audio_data_drain(AudioData *a, int nb_samples); + +/** + * Add samples in AudioData to an AVAudioFifo. + * + * @param af Audio FIFO Buffer + * @param a AudioData struct + * @param offset number of samples to skip from the start of the data + * @param nb_samples number of samples to add to the FIFO + * @return number of samples actually added to the FIFO, or + * negative AVERROR code on error + */ +int ff_audio_data_add_to_fifo(AVAudioFifo *af, AudioData *a, int offset, + int nb_samples); + +/** + * Read samples from an AVAudioFifo to AudioData. + * + * @param af Audio FIFO Buffer + * @param a AudioData struct + * @param nb_samples number of samples to read from the FIFO + * @return number of samples actually read from the FIFO, or + * negative AVERROR code on error + */ +int ff_audio_data_read_from_fifo(AVAudioFifo *af, AudioData *a, int nb_samples); + +#endif /* AVRESAMPLE_AUDIO_DATA_H */ diff --git a/lib/ffmpeg/libavresample/audio_mix.c b/lib/ffmpeg/libavresample/audio_mix.c new file mode 100644 index 0000000000..b69bfbcf3e --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_mix.c @@ -0,0 +1,739 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> + +#include "libavutil/common.h" +#include "libavutil/libm.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" +#include "audio_mix.h" + +static const char *coeff_type_names[] = { "q8", "q15", "flt" }; + +struct AudioMix { + AVAudioResampleContext *avr; + enum AVSampleFormat fmt; + enum AVMixCoeffType coeff_type; + uint64_t in_layout; + uint64_t out_layout; + int in_channels; + int out_channels; + + int ptr_align; + int samples_align; + int has_optimized_func; + const char *func_descr; + const char *func_descr_generic; + mix_func *mix; + mix_func *mix_generic; + + int in_matrix_channels; + int out_matrix_channels; + int output_zero[AVRESAMPLE_MAX_CHANNELS]; + int input_skip[AVRESAMPLE_MAX_CHANNELS]; + int output_skip[AVRESAMPLE_MAX_CHANNELS]; + int16_t *matrix_q8[AVRESAMPLE_MAX_CHANNELS]; + int32_t *matrix_q15[AVRESAMPLE_MAX_CHANNELS]; + float *matrix_flt[AVRESAMPLE_MAX_CHANNELS]; + void **matrix; +}; + +void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt, + enum AVMixCoeffType coeff_type, int in_channels, + int out_channels, int ptr_align, int samples_align, + const char *descr, void *mix_func) +{ + if (fmt == am->fmt && coeff_type == am->coeff_type && + ( in_channels == am->in_matrix_channels || in_channels == 0) && + (out_channels == am->out_matrix_channels || out_channels == 0)) { + char chan_str[16]; + am->mix = mix_func; + am->func_descr = descr; + am->ptr_align = ptr_align; + am->samples_align = samples_align; + if (ptr_align == 1 && samples_align == 1) { + am->mix_generic = mix_func; + am->func_descr_generic = descr; + } else { + am->has_optimized_func = 1; + } + if (in_channels) { + if (out_channels) + snprintf(chan_str, sizeof(chan_str), "[%d to %d] ", + in_channels, out_channels); + else + snprintf(chan_str, sizeof(chan_str), "[%d to any] ", + in_channels); + } else if (out_channels) { + snprintf(chan_str, sizeof(chan_str), "[any to %d] ", + out_channels); + } else { + snprintf(chan_str, sizeof(chan_str), "[any to any] "); + } + av_log(am->avr, AV_LOG_DEBUG, "audio_mix: found function: [fmt=%s] " + "[c=%s] %s(%s)\n", av_get_sample_fmt_name(fmt), + coeff_type_names[coeff_type], chan_str, descr); + } +} + +#define MIX_FUNC_NAME(fmt, cfmt) mix_any_ ## fmt ##_## cfmt ##_c + +#define MIX_FUNC_GENERIC(fmt, cfmt, stype, ctype, sumtype, expr) \ +static void MIX_FUNC_NAME(fmt, cfmt)(stype **samples, ctype **matrix, \ + int len, int out_ch, int in_ch) \ +{ \ + int i, in, out; \ + stype temp[AVRESAMPLE_MAX_CHANNELS]; \ + for (i = 0; i < len; i++) { \ + for (out = 0; out < out_ch; out++) { \ + sumtype sum = 0; \ + for (in = 0; in < in_ch; in++) \ + sum += samples[in][i] * matrix[out][in]; \ + temp[out] = expr; \ + } \ + for (out = 0; out < out_ch; out++) \ + samples[out][i] = temp[out]; \ + } \ +} + +MIX_FUNC_GENERIC(FLTP, FLT, float, float, float, sum) +MIX_FUNC_GENERIC(S16P, FLT, int16_t, float, float, av_clip_int16(lrintf(sum))) +MIX_FUNC_GENERIC(S16P, Q15, int16_t, int32_t, int64_t, av_clip_int16(sum >> 15)) +MIX_FUNC_GENERIC(S16P, Q8, int16_t, int16_t, int32_t, av_clip_int16(sum >> 8)) + +/* TODO: templatize the channel-specific C functions */ + +static void mix_2_to_1_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float *src0 = samples[0]; + float *src1 = samples[1]; + float *dst = src0; + float m0 = matrix[0][0]; + float m1 = matrix[0][1]; + + while (len > 4) { + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + *dst++ = *src0++ * m0 + *src1++ * m1; + len -= 4; + } + while (len > 0) { + *dst++ = *src0++ * m0 + *src1++ * m1; + len--; + } +} + +static void mix_2_to_1_s16p_flt_c(int16_t **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + int16_t *src0 = samples[0]; + int16_t *src1 = samples[1]; + int16_t *dst = src0; + float m0 = matrix[0][0]; + float m1 = matrix[0][1]; + + while (len > 4) { + *dst++ = av_clip_int16(lrintf(*src0++ * m0 + *src1++ * m1)); + *dst++ = av_clip_int16(lrintf(*src0++ * m0 + *src1++ * m1)); + *dst++ = av_clip_int16(lrintf(*src0++ * m0 + *src1++ * m1)); + *dst++ = av_clip_int16(lrintf(*src0++ * m0 + *src1++ * m1)); + len -= 4; + } + while (len > 0) { + *dst++ = av_clip_int16(lrintf(*src0++ * m0 + *src1++ * m1)); + len--; + } +} + +static void mix_2_to_1_s16p_q8_c(int16_t **samples, int16_t **matrix, int len, + int out_ch, int in_ch) +{ + int16_t *src0 = samples[0]; + int16_t *src1 = samples[1]; + int16_t *dst = src0; + int16_t m0 = matrix[0][0]; + int16_t m1 = matrix[0][1]; + + while (len > 4) { + *dst++ = (*src0++ * m0 + *src1++ * m1) >> 8; + *dst++ = (*src0++ * m0 + *src1++ * m1) >> 8; + *dst++ = (*src0++ * m0 + *src1++ * m1) >> 8; + *dst++ = (*src0++ * m0 + *src1++ * m1) >> 8; + len -= 4; + } + while (len > 0) { + *dst++ = (*src0++ * m0 + *src1++ * m1) >> 8; + len--; + } +} + +static void mix_1_to_2_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v; + float *dst0 = samples[0]; + float *dst1 = samples[1]; + float *src = dst0; + float m0 = matrix[0][0]; + float m1 = matrix[1][0]; + + while (len > 4) { + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + len -= 4; + } + while (len > 0) { + v = *src++; + *dst0++ = v * m1; + *dst1++ = v * m0; + len--; + } +} + +static void mix_6_to_2_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v0, v1; + float *src0 = samples[0]; + float *src1 = samples[1]; + float *src2 = samples[2]; + float *src3 = samples[3]; + float *src4 = samples[4]; + float *src5 = samples[5]; + float *dst0 = src0; + float *dst1 = src1; + float *m0 = matrix[0]; + float *m1 = matrix[1]; + + while (len > 0) { + v0 = *src0++; + v1 = *src1++; + *dst0++ = v0 * m0[0] + + v1 * m0[1] + + *src2 * m0[2] + + *src3 * m0[3] + + *src4 * m0[4] + + *src5 * m0[5]; + *dst1++ = v0 * m1[0] + + v1 * m1[1] + + *src2++ * m1[2] + + *src3++ * m1[3] + + *src4++ * m1[4] + + *src5++ * m1[5]; + len--; + } +} + +static void mix_2_to_6_fltp_flt_c(float **samples, float **matrix, int len, + int out_ch, int in_ch) +{ + float v0, v1; + float *dst0 = samples[0]; + float *dst1 = samples[1]; + float *dst2 = samples[2]; + float *dst3 = samples[3]; + float *dst4 = samples[4]; + float *dst5 = samples[5]; + float *src0 = dst0; + float *src1 = dst1; + + while (len > 0) { + v0 = *src0++; + v1 = *src1++; + *dst0++ = v0 * matrix[0][0] + v1 * matrix[0][1]; + *dst1++ = v0 * matrix[1][0] + v1 * matrix[1][1]; + *dst2++ = v0 * matrix[2][0] + v1 * matrix[2][1]; + *dst3++ = v0 * matrix[3][0] + v1 * matrix[3][1]; + *dst4++ = v0 * matrix[4][0] + v1 * matrix[4][1]; + *dst5++ = v0 * matrix[5][0] + v1 * matrix[5][1]; + len--; + } +} + +static int mix_function_init(AudioMix *am) +{ + am->func_descr = am->func_descr_generic = "n/a"; + am->mix = am->mix_generic = NULL; + + /* no need to set a mix function when we're skipping mixing */ + if (!am->in_matrix_channels || !am->out_matrix_channels) + return 0; + + /* any-to-any C versions */ + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(FLTP, FLT)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, FLT)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q15, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q15)); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q8, + 0, 0, 1, 1, "C", MIX_FUNC_NAME(S16P, Q8)); + + /* channel-specific C versions */ + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 1, 1, "C", mix_2_to_1_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 1, 1, "C", mix_2_to_1_s16p_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q8, + 2, 1, 1, 1, "C", mix_2_to_1_s16p_q8_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 1, 1, "C", mix_1_to_2_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 6, 2, 1, 1, "C", mix_6_to_2_fltp_flt_c); + + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 6, 1, 1, "C", mix_2_to_6_fltp_flt_c); + + if (ARCH_X86) + ff_audio_mix_init_x86(am); + + if (!am->mix) { + av_log(am->avr, AV_LOG_ERROR, "audio_mix: NO FUNCTION FOUND: [fmt=%s] " + "[c=%s] [%d to %d]\n", av_get_sample_fmt_name(am->fmt), + coeff_type_names[am->coeff_type], am->in_channels, + am->out_channels); + return AVERROR_PATCHWELCOME; + } + return 0; +} + +AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr) +{ + AudioMix *am; + int ret; + + am = av_mallocz(sizeof(*am)); + if (!am) + return NULL; + am->avr = avr; + + if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P && + avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP) { + av_log(avr, AV_LOG_ERROR, "Unsupported internal format for " + "mixing: %s\n", + av_get_sample_fmt_name(avr->internal_sample_fmt)); + goto error; + } + + am->fmt = avr->internal_sample_fmt; + am->coeff_type = avr->mix_coeff_type; + am->in_layout = avr->in_channel_layout; + am->out_layout = avr->out_channel_layout; + am->in_channels = avr->in_channels; + am->out_channels = avr->out_channels; + + /* build matrix if the user did not already set one */ + if (avr->mix_matrix) { + ret = ff_audio_mix_set_matrix(am, avr->mix_matrix, avr->in_channels); + if (ret < 0) + goto error; + av_freep(&avr->mix_matrix); + } else { + double *matrix_dbl = av_mallocz(avr->out_channels * avr->in_channels * + sizeof(*matrix_dbl)); + if (!matrix_dbl) + goto error; + + ret = avresample_build_matrix(avr->in_channel_layout, + avr->out_channel_layout, + avr->center_mix_level, + avr->surround_mix_level, + avr->lfe_mix_level, + avr->normalize_mix_level, + matrix_dbl, + avr->in_channels, + avr->matrix_encoding); + if (ret < 0) { + av_free(matrix_dbl); + goto error; + } + + ret = ff_audio_mix_set_matrix(am, matrix_dbl, avr->in_channels); + if (ret < 0) { + av_log(avr, AV_LOG_ERROR, "error setting mix matrix\n"); + av_free(matrix_dbl); + goto error; + } + + av_free(matrix_dbl); + } + + return am; + +error: + av_free(am); + return NULL; +} + +void ff_audio_mix_free(AudioMix **am_p) +{ + AudioMix *am; + + if (!*am_p) + return; + am = *am_p; + + if (am->matrix) { + av_free(am->matrix[0]); + am->matrix = NULL; + } + memset(am->matrix_q8, 0, sizeof(am->matrix_q8 )); + memset(am->matrix_q15, 0, sizeof(am->matrix_q15)); + memset(am->matrix_flt, 0, sizeof(am->matrix_flt)); + + av_freep(am_p); +} + +int ff_audio_mix(AudioMix *am, AudioData *src) +{ + int use_generic = 1; + int len = src->nb_samples; + int i, j; + + /* determine whether to use the optimized function based on pointer and + samples alignment in both the input and output */ + if (am->has_optimized_func) { + int aligned_len = FFALIGN(len, am->samples_align); + if (!(src->ptr_align % am->ptr_align) && + src->samples_align >= aligned_len) { + len = aligned_len; + use_generic = 0; + } + } + av_dlog(am->avr, "audio_mix: %d samples - %d to %d channels (%s)\n", + src->nb_samples, am->in_channels, am->out_channels, + use_generic ? am->func_descr_generic : am->func_descr); + + if (am->in_matrix_channels && am->out_matrix_channels) { + uint8_t **data; + uint8_t *data0[AVRESAMPLE_MAX_CHANNELS]; + + if (am->out_matrix_channels < am->out_channels || + am->in_matrix_channels < am->in_channels) { + for (i = 0, j = 0; i < FFMAX(am->in_channels, am->out_channels); i++) { + if (am->input_skip[i] || am->output_skip[i] || am->output_zero[i]) + continue; + data0[j++] = src->data[i]; + } + data = data0; + } else { + data = src->data; + } + + if (use_generic) + am->mix_generic(data, am->matrix, len, am->out_matrix_channels, + am->in_matrix_channels); + else + am->mix(data, am->matrix, len, am->out_matrix_channels, + am->in_matrix_channels); + } + + if (am->out_matrix_channels < am->out_channels) { + for (i = 0; i < am->out_channels; i++) + if (am->output_zero[i]) + av_samples_set_silence(&src->data[i], 0, len, 1, am->fmt); + } + + ff_audio_data_set_channels(src, am->out_channels); + + return 0; +} + +int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride) +{ + int i, o, i0, o0; + + if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || + am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n"); + return AVERROR(EINVAL); + } + +#define GET_MATRIX_CONVERT(suffix, scale) \ + if (!am->matrix_ ## suffix[0]) { \ + av_log(am->avr, AV_LOG_ERROR, "matrix is not set\n"); \ + return AVERROR(EINVAL); \ + } \ + for (o = 0, o0 = 0; o < am->out_channels; o++) { \ + for (i = 0, i0 = 0; i < am->in_channels; i++) { \ + if (am->input_skip[i] || am->output_zero[o]) \ + matrix[o * stride + i] = 0.0; \ + else \ + matrix[o * stride + i] = am->matrix_ ## suffix[o0][i0] * \ + (scale); \ + if (!am->input_skip[i]) \ + i0++; \ + } \ + if (!am->output_zero[o]) \ + o0++; \ + } + + switch (am->coeff_type) { + case AV_MIX_COEFF_TYPE_Q8: + GET_MATRIX_CONVERT(q8, 1.0 / 256.0); + break; + case AV_MIX_COEFF_TYPE_Q15: + GET_MATRIX_CONVERT(q15, 1.0 / 32768.0); + break; + case AV_MIX_COEFF_TYPE_FLT: + GET_MATRIX_CONVERT(flt, 1.0); + break; + default: + av_log(am->avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static void reduce_matrix(AudioMix *am, const double *matrix, int stride) +{ + int i, o; + + memset(am->output_zero, 0, sizeof(am->output_zero)); + memset(am->input_skip, 0, sizeof(am->input_skip)); + memset(am->output_skip, 0, sizeof(am->output_skip)); + + /* exclude output channels if they can be zeroed instead of mixed */ + for (o = 0; o < am->out_channels; o++) { + int zero = 1; + + /* check if the output is always silent */ + for (i = 0; i < am->in_channels; i++) { + if (matrix[o * stride + i] != 0.0) { + zero = 0; + break; + } + } + /* check if the corresponding input channel makes a contribution to + any output channel */ + if (o < am->in_channels) { + for (i = 0; i < am->out_channels; i++) { + if (matrix[i * stride + o] != 0.0) { + zero = 0; + break; + } + } + } + if (zero) { + am->output_zero[o] = 1; + am->out_matrix_channels--; + } + } + if (am->out_matrix_channels == 0) { + am->in_matrix_channels = 0; + return; + } + + /* skip input channels that contribute fully only to the corresponding + output channel */ + for (i = 0; i < FFMIN(am->in_channels, am->out_channels); i++) { + int skip = 1; + + for (o = 0; o < am->out_channels; o++) { + int i0; + if ((o != i && matrix[o * stride + i] != 0.0) || + (o == i && matrix[o * stride + i] != 1.0)) { + skip = 0; + break; + } + /* if the input contributes fully to the output, also check that no + other inputs contribute to this output */ + if (o == i) { + for (i0 = 0; i0 < am->in_channels; i0++) { + if (i0 != i && matrix[o * stride + i0] != 0.0) { + skip = 0; + break; + } + } + } + } + if (skip) { + am->input_skip[i] = 1; + am->in_matrix_channels--; + } + } + /* skip input channels that do not contribute to any output channel */ + for (; i < am->in_channels; i++) { + int contrib = 0; + + for (o = 0; o < am->out_channels; o++) { + if (matrix[o * stride + i] != 0.0) { + contrib = 1; + break; + } + } + if (!contrib) { + am->input_skip[i] = 1; + am->in_matrix_channels--; + } + } + if (am->in_matrix_channels == 0) { + am->out_matrix_channels = 0; + return; + } + + /* skip output channels that only get full contribution from the + corresponding input channel */ + for (o = 0; o < FFMIN(am->in_channels, am->out_channels); o++) { + int skip = 1; + int o0; + + for (i = 0; i < am->in_channels; i++) { + if ((o != i && matrix[o * stride + i] != 0.0) || + (o == i && matrix[o * stride + i] != 1.0)) { + skip = 0; + break; + } + } + /* check if the corresponding input channel makes a contribution to + any other output channel */ + i = o; + for (o0 = 0; o0 < am->out_channels; o0++) { + if (o0 != i && matrix[o0 * stride + i] != 0.0) { + skip = 0; + break; + } + } + if (skip) { + am->output_skip[o] = 1; + am->out_matrix_channels--; + } + } + if (am->out_matrix_channels == 0) { + am->in_matrix_channels = 0; + return; + } +} + +int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride) +{ + int i, o, i0, o0, ret; + char in_layout_name[128]; + char out_layout_name[128]; + + if ( am->in_channels <= 0 || am->in_channels > AVRESAMPLE_MAX_CHANNELS || + am->out_channels <= 0 || am->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(am->avr, AV_LOG_ERROR, "Invalid channel counts\n"); + return AVERROR(EINVAL); + } + + if (am->matrix) { + av_free(am->matrix[0]); + am->matrix = NULL; + } + + am->in_matrix_channels = am->in_channels; + am->out_matrix_channels = am->out_channels; + + reduce_matrix(am, matrix, stride); + +#define CONVERT_MATRIX(type, expr) \ + am->matrix_## type[0] = av_mallocz(am->out_matrix_channels * \ + am->in_matrix_channels * \ + sizeof(*am->matrix_## type[0])); \ + if (!am->matrix_## type[0]) \ + return AVERROR(ENOMEM); \ + for (o = 0, o0 = 0; o < am->out_channels; o++) { \ + if (am->output_zero[o] || am->output_skip[o]) \ + continue; \ + if (o0 > 0) \ + am->matrix_## type[o0] = am->matrix_## type[o0 - 1] + \ + am->in_matrix_channels; \ + for (i = 0, i0 = 0; i < am->in_channels; i++) { \ + double v; \ + if (am->input_skip[i]) \ + continue; \ + v = matrix[o * stride + i]; \ + am->matrix_## type[o0][i0] = expr; \ + i0++; \ + } \ + o0++; \ + } \ + am->matrix = (void **)am->matrix_## type; + + if (am->in_matrix_channels && am->out_matrix_channels) { + switch (am->coeff_type) { + case AV_MIX_COEFF_TYPE_Q8: + CONVERT_MATRIX(q8, av_clip_int16(lrint(256.0 * v))) + break; + case AV_MIX_COEFF_TYPE_Q15: + CONVERT_MATRIX(q15, av_clipl_int32(llrint(32768.0 * v))) + break; + case AV_MIX_COEFF_TYPE_FLT: + CONVERT_MATRIX(flt, v) + break; + default: + av_log(am->avr, AV_LOG_ERROR, "Invalid mix coeff type\n"); + return AVERROR(EINVAL); + } + } + + ret = mix_function_init(am); + if (ret < 0) + return ret; + + av_get_channel_layout_string(in_layout_name, sizeof(in_layout_name), + am->in_channels, am->in_layout); + av_get_channel_layout_string(out_layout_name, sizeof(out_layout_name), + am->out_channels, am->out_layout); + av_log(am->avr, AV_LOG_DEBUG, "audio_mix: %s to %s\n", + in_layout_name, out_layout_name); + av_log(am->avr, AV_LOG_DEBUG, "matrix size: %d x %d\n", + am->in_matrix_channels, am->out_matrix_channels); + for (o = 0; o < am->out_channels; o++) { + for (i = 0; i < am->in_channels; i++) { + if (am->output_zero[o]) + av_log(am->avr, AV_LOG_DEBUG, " (ZERO)"); + else if (am->input_skip[i] || am->output_skip[o]) + av_log(am->avr, AV_LOG_DEBUG, " (SKIP)"); + else + av_log(am->avr, AV_LOG_DEBUG, " %0.3f ", + matrix[o * am->in_channels + i]); + } + av_log(am->avr, AV_LOG_DEBUG, "\n"); + } + + return 0; +} diff --git a/lib/ffmpeg/libavresample/audio_mix.h b/lib/ffmpeg/libavresample/audio_mix.h new file mode 100644 index 0000000000..5bae5ab6da --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_mix.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AUDIO_MIX_H +#define AVRESAMPLE_AUDIO_MIX_H + +#include <stdint.h> + +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" + +typedef void (mix_func)(uint8_t **src, void **matrix, int len, int out_ch, + int in_ch); + +/** + * Set mixing function if the parameters match. + * + * This compares the parameters of the mixing function to the parameters in the + * AudioMix context. If the parameters do not match, no changes are made to the + * active functions. If the parameters do match and the alignment is not + * constrained, the function is set as the generic mixing function. If the + * parameters match and the alignment is constrained, the function is set as + * the optimized mixing function. + * + * @param am AudioMix context + * @param fmt input/output sample format + * @param coeff_type mixing coefficient type + * @param in_channels number of input channels, or 0 for any number of channels + * @param out_channels number of output channels, or 0 for any number of channels + * @param ptr_align buffer pointer alignment, in bytes + * @param samples_align buffer size alignment, in samples + * @param descr function type description (e.g. "C" or "SSE") + * @param mix_func mixing function pointer + */ +void ff_audio_mix_set_func(AudioMix *am, enum AVSampleFormat fmt, + enum AVMixCoeffType coeff_type, int in_channels, + int out_channels, int ptr_align, int samples_align, + const char *descr, void *mix_func); + +/** + * Allocate and initialize an AudioMix context. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * AudioMix context. + * + * @param avr AVAudioResampleContext + * @return newly-allocated AudioMix context. + */ +AudioMix *ff_audio_mix_alloc(AVAudioResampleContext *avr); + +/** + * Free an AudioMix context. + */ +void ff_audio_mix_free(AudioMix **am); + +/** + * Apply channel mixing to audio data using the current mixing matrix. + */ +int ff_audio_mix(AudioMix *am, AudioData *src); + +/** + * Get the current mixing matrix. + */ +int ff_audio_mix_get_matrix(AudioMix *am, double *matrix, int stride); + +/** + * Set the current mixing matrix. + */ +int ff_audio_mix_set_matrix(AudioMix *am, const double *matrix, int stride); + +/* arch-specific initialization functions */ + +void ff_audio_mix_init_x86(AudioMix *am); + +#endif /* AVRESAMPLE_AUDIO_MIX_H */ diff --git a/lib/ffmpeg/libavresample/audio_mix_matrix.c b/lib/ffmpeg/libavresample/audio_mix_matrix.c new file mode 100644 index 0000000000..8da1b487a4 --- /dev/null +++ b/lib/ffmpeg/libavresample/audio_mix_matrix.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2011 Michael Niedermayer (michaelni@gmx.at) + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> + +#include "libavutil/common.h" +#include "libavutil/libm.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" +#include "audio_mix.h" + +/* channel positions */ +#define FRONT_LEFT 0 +#define FRONT_RIGHT 1 +#define FRONT_CENTER 2 +#define LOW_FREQUENCY 3 +#define BACK_LEFT 4 +#define BACK_RIGHT 5 +#define FRONT_LEFT_OF_CENTER 6 +#define FRONT_RIGHT_OF_CENTER 7 +#define BACK_CENTER 8 +#define SIDE_LEFT 9 +#define SIDE_RIGHT 10 +#define TOP_CENTER 11 +#define TOP_FRONT_LEFT 12 +#define TOP_FRONT_CENTER 13 +#define TOP_FRONT_RIGHT 14 +#define TOP_BACK_LEFT 15 +#define TOP_BACK_CENTER 16 +#define TOP_BACK_RIGHT 17 +#define STEREO_LEFT 29 +#define STEREO_RIGHT 30 +#define WIDE_LEFT 31 +#define WIDE_RIGHT 32 +#define SURROUND_DIRECT_LEFT 33 +#define SURROUND_DIRECT_RIGHT 34 +#define LOW_FREQUENCY_2 35 + +#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ + +static av_always_inline int even(uint64_t layout) +{ + return (!layout || (layout & (layout - 1))); +} + +static int sane_layout(uint64_t layout) +{ + /* check that there is at least 1 front speaker */ + if (!(layout & AV_CH_LAYOUT_SURROUND)) + return 0; + + /* check for left/right symmetry */ + if (!even(layout & (AV_CH_FRONT_LEFT | AV_CH_FRONT_RIGHT)) || + !even(layout & (AV_CH_SIDE_LEFT | AV_CH_SIDE_RIGHT)) || + !even(layout & (AV_CH_BACK_LEFT | AV_CH_BACK_RIGHT)) || + !even(layout & (AV_CH_FRONT_LEFT_OF_CENTER | AV_CH_FRONT_RIGHT_OF_CENTER)) || + !even(layout & (AV_CH_TOP_FRONT_LEFT | AV_CH_TOP_FRONT_RIGHT)) || + !even(layout & (AV_CH_TOP_BACK_LEFT | AV_CH_TOP_BACK_RIGHT)) || + !even(layout & (AV_CH_STEREO_LEFT | AV_CH_STEREO_RIGHT)) || + !even(layout & (AV_CH_WIDE_LEFT | AV_CH_WIDE_RIGHT)) || + !even(layout & (AV_CH_SURROUND_DIRECT_LEFT | AV_CH_SURROUND_DIRECT_RIGHT))) + return 0; + + return 1; +} + +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, + double *matrix_out, int stride, + enum AVMatrixEncoding matrix_encoding) +{ + int i, j, out_i, out_j; + double matrix[64][64] = {{0}}; + int64_t unaccounted; + double maxcoef = 0; + int in_channels, out_channels; + + if ((out_layout & AV_CH_LAYOUT_STEREO_DOWNMIX) == AV_CH_LAYOUT_STEREO_DOWNMIX) { + out_layout = AV_CH_LAYOUT_STEREO; + } + + unaccounted = in_layout & ~out_layout; + + in_channels = av_get_channel_layout_nb_channels( in_layout); + out_channels = av_get_channel_layout_nb_channels(out_layout); + + memset(matrix_out, 0, out_channels * stride * sizeof(*matrix_out)); + + /* check if layouts are supported */ + if (!in_layout || in_channels > AVRESAMPLE_MAX_CHANNELS) + return AVERROR(EINVAL); + if (!out_layout || out_channels > AVRESAMPLE_MAX_CHANNELS) + return AVERROR(EINVAL); + + /* check if layouts are unbalanced or abnormal */ + if (!sane_layout(in_layout) || !sane_layout(out_layout)) + return AVERROR_PATCHWELCOME; + + /* route matching input/output channels */ + for (i = 0; i < 64; i++) { + if (in_layout & out_layout & (1ULL << i)) + matrix[i][i] = 1.0; + } + + /* mix front center to front left/right */ + if (unaccounted & AV_CH_FRONT_CENTER) { + if ((out_layout & AV_CH_LAYOUT_STEREO) == AV_CH_LAYOUT_STEREO) { + matrix[FRONT_LEFT ][FRONT_CENTER] += M_SQRT1_2; + matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix front left/right to center */ + if (unaccounted & AV_CH_LAYOUT_STEREO) { + if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][FRONT_LEFT ] += M_SQRT1_2; + matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2; + /* mix left/right/center to center */ + if (in_layout & AV_CH_FRONT_CENTER) + matrix[FRONT_CENTER][FRONT_CENTER] = center_mix_level * M_SQRT2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix back center to back, side, or front */ + if (unaccounted & AV_CH_BACK_CENTER) { + if (out_layout & AV_CH_BACK_LEFT) { + matrix[BACK_LEFT ][BACK_CENTER] += M_SQRT1_2; + matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2; + } else if (out_layout & AV_CH_SIDE_LEFT) { + matrix[SIDE_LEFT ][BACK_CENTER] += M_SQRT1_2; + matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2; + } else if (out_layout & AV_CH_FRONT_LEFT) { + 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] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + } else { + matrix[FRONT_LEFT ][BACK_CENTER] -= surround_mix_level; + matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level; + } + } else { + matrix[FRONT_LEFT ][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + } + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][BACK_CENTER] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix back left/right to back center, side, or front */ + if (unaccounted & AV_CH_BACK_LEFT) { + if (out_layout & AV_CH_BACK_CENTER) { + matrix[BACK_CENTER][BACK_LEFT ] += M_SQRT1_2; + matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2; + } else if (out_layout & AV_CH_SIDE_LEFT) { + /* if side channels do not exist in the input, just copy back + channels to side channels, otherwise mix back into side */ + if (in_layout & AV_CH_SIDE_LEFT) { + matrix[SIDE_LEFT ][BACK_LEFT ] += M_SQRT1_2; + matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2; + } else { + matrix[SIDE_LEFT ][BACK_LEFT ] += 1.0; + matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0; + } + } else if (out_layout & AV_CH_FRONT_LEFT) { + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + matrix[FRONT_LEFT ][BACK_LEFT ] -= surround_mix_level * SQRT3_2; + matrix[FRONT_LEFT ][BACK_RIGHT] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level * SQRT3_2; + } else { + matrix[FRONT_LEFT ][BACK_LEFT ] += surround_mix_level; + matrix[FRONT_RIGHT][BACK_RIGHT] += surround_mix_level; + } + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][BACK_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_CENTER][BACK_RIGHT] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix side left/right into back or front */ + if (unaccounted & AV_CH_SIDE_LEFT) { + if (out_layout & AV_CH_BACK_LEFT) { + /* if back channels do not exist in the input, just copy side + channels to back channels, otherwise mix side into back */ + if (in_layout & AV_CH_BACK_LEFT) { + matrix[BACK_LEFT ][SIDE_LEFT ] += M_SQRT1_2; + matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2; + } else { + matrix[BACK_LEFT ][SIDE_LEFT ] += 1.0; + matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0; + } + } else if (out_layout & AV_CH_BACK_CENTER) { + matrix[BACK_CENTER][SIDE_LEFT ] += M_SQRT1_2; + matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2; + } else if (out_layout & AV_CH_FRONT_LEFT) { + if (matrix_encoding == AV_MATRIX_ENCODING_DOLBY) { + matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2; + } else if (matrix_encoding == AV_MATRIX_ENCODING_DPLII) { + matrix[FRONT_LEFT ][SIDE_LEFT ] -= surround_mix_level * SQRT3_2; + matrix[FRONT_LEFT ][SIDE_RIGHT] -= surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level * SQRT3_2; + } else { + matrix[FRONT_LEFT ][SIDE_LEFT ] += surround_mix_level; + matrix[FRONT_RIGHT][SIDE_RIGHT] += surround_mix_level; + } + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][SIDE_LEFT ] += surround_mix_level * M_SQRT1_2; + matrix[FRONT_CENTER][SIDE_RIGHT] += surround_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix left-of-center/right-of-center into front left/right or center */ + if (unaccounted & AV_CH_FRONT_LEFT_OF_CENTER) { + if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][FRONT_LEFT_OF_CENTER ] += 1.0; + matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0; + } else if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER ] += M_SQRT1_2; + matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + /* mix LFE into front left/right or center */ + if (unaccounted & AV_CH_LOW_FREQUENCY) { + if (out_layout & AV_CH_FRONT_CENTER) { + matrix[FRONT_CENTER][LOW_FREQUENCY] += lfe_mix_level; + } else if (out_layout & AV_CH_FRONT_LEFT) { + matrix[FRONT_LEFT ][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; + matrix[FRONT_RIGHT][LOW_FREQUENCY] += lfe_mix_level * M_SQRT1_2; + } else + return AVERROR_PATCHWELCOME; + } + + /* transfer internal matrix to output matrix and calculate maximum + per-channel coefficient sum */ + for (out_i = i = 0; out_i < out_channels && i < 64; i++) { + double sum = 0; + for (out_j = j = 0; out_j < in_channels && j < 64; j++) { + matrix_out[out_i * stride + out_j] = matrix[i][j]; + sum += fabs(matrix[i][j]); + if (in_layout & (1ULL << j)) + out_j++; + } + maxcoef = FFMAX(maxcoef, sum); + if (out_layout & (1ULL << i)) + out_i++; + } + + /* normalize */ + if (normalize && maxcoef > 1.0) { + for (i = 0; i < out_channels; i++) + for (j = 0; j < in_channels; j++) + matrix_out[i * stride + j] /= maxcoef; + } + + return 0; +} diff --git a/lib/ffmpeg/libavresample/avresample-test.c b/lib/ffmpeg/libavresample/avresample-test.c new file mode 100644 index 0000000000..81e9bf0f50 --- /dev/null +++ b/lib/ffmpeg/libavresample/avresample-test.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2002 Fabrice Bellard + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdint.h> +#include <stdio.h> + +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include "libavutil/lfg.h" +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" + +static double dbl_rand(AVLFG *lfg) +{ + return 2.0 * (av_lfg_get(lfg) / (double)UINT_MAX) - 1.0; +} + +#define PUT_FUNC(name, fmt, type, expr) \ +static void put_sample_ ## name(void **data, enum AVSampleFormat sample_fmt,\ + int channels, int sample, int ch, \ + double v_dbl) \ +{ \ + type v = expr; \ + type **out = (type **)data; \ + if (av_sample_fmt_is_planar(sample_fmt)) \ + out[ch][sample] = v; \ + else \ + out[0][sample * channels + ch] = v; \ +} + +PUT_FUNC(u8, AV_SAMPLE_FMT_U8, uint8_t, av_clip_uint8 ( lrint(v_dbl * (1 << 7)) + 128)) +PUT_FUNC(s16, AV_SAMPLE_FMT_S16, int16_t, av_clip_int16 ( lrint(v_dbl * (1 << 15)))) +PUT_FUNC(s32, AV_SAMPLE_FMT_S32, int32_t, av_clipl_int32(llrint(v_dbl * (1U << 31)))) +PUT_FUNC(flt, AV_SAMPLE_FMT_FLT, float, v_dbl) +PUT_FUNC(dbl, AV_SAMPLE_FMT_DBL, double, v_dbl) + +static void put_sample(void **data, enum AVSampleFormat sample_fmt, + int channels, int sample, int ch, double v_dbl) +{ + switch (av_get_packed_sample_fmt(sample_fmt)) { + case AV_SAMPLE_FMT_U8: + put_sample_u8(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_S16: + put_sample_s16(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_S32: + put_sample_s32(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_FLT: + put_sample_flt(data, sample_fmt, channels, sample, ch, v_dbl); + break; + case AV_SAMPLE_FMT_DBL: + put_sample_dbl(data, sample_fmt, channels, sample, ch, v_dbl); + break; + } +} + +static void audiogen(AVLFG *rnd, void **data, enum AVSampleFormat sample_fmt, + int channels, int sample_rate, int nb_samples) +{ + int i, ch, k; + double v, f, a, ampa; + double tabf1[AVRESAMPLE_MAX_CHANNELS]; + double tabf2[AVRESAMPLE_MAX_CHANNELS]; + double taba[AVRESAMPLE_MAX_CHANNELS]; + +#define PUT_SAMPLE put_sample(data, sample_fmt, channels, k, ch, v); + + k = 0; + + /* 1 second of single freq sinus at 1000 Hz */ + a = 0; + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + v = sin(a) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + a += M_PI * 1000.0 * 2.0 / sample_rate; + } + + /* 1 second of varying frequency between 100 and 10000 Hz */ + a = 0; + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + v = sin(a) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + f = 100.0 + (((10000.0 - 100.0) * i) / sample_rate); + a += M_PI * f * 2.0 / sample_rate; + } + + /* 0.5 second of low amplitude white noise */ + for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { + v = dbl_rand(rnd) * 0.30; + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + } + + /* 0.5 second of high amplitude white noise */ + for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { + v = dbl_rand(rnd); + for (ch = 0; ch < channels; ch++) + PUT_SAMPLE + } + + /* 1 second of unrelated ramps for each channel */ + for (ch = 0; ch < channels; ch++) { + taba[ch] = 0; + tabf1[ch] = 100 + av_lfg_get(rnd) % 5000; + tabf2[ch] = 100 + av_lfg_get(rnd) % 5000; + } + for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { + for (ch = 0; ch < channels; ch++) { + v = sin(taba[ch]) * 0.30; + PUT_SAMPLE + f = tabf1[ch] + (((tabf2[ch] - tabf1[ch]) * i) / sample_rate); + taba[ch] += M_PI * f * 2.0 / sample_rate; + } + } + + /* 2 seconds of 500 Hz with varying volume */ + a = 0; + ampa = 0; + for (i = 0; i < 2 * sample_rate && k < nb_samples; i++, k++) { + for (ch = 0; ch < channels; ch++) { + double amp = (1.0 + sin(ampa)) * 0.15; + if (ch & 1) + amp = 0.30 - amp; + v = sin(a) * amp; + PUT_SAMPLE + a += M_PI * 500.0 * 2.0 / sample_rate; + ampa += M_PI * 2.0 / sample_rate; + } + } +} + +/* formats, rates, and layouts are ordered for priority in testing. + e.g. 'avresample-test 4 2 2' will test all input/output combinations of + S16/FLTP/S16P/FLT, 48000/44100, and stereo/mono */ + +static const enum AVSampleFormat formats[] = { + AV_SAMPLE_FMT_S16, + AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_FLT, + AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_S32, + AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_U8, + AV_SAMPLE_FMT_DBLP, + AV_SAMPLE_FMT_DBL, +}; + +static const int rates[] = { + 48000, + 44100, + 16000 +}; + +static const uint64_t layouts[] = { + AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_MONO, + AV_CH_LAYOUT_5POINT1, + AV_CH_LAYOUT_7POINT1, +}; + +int main(int argc, char **argv) +{ + AVAudioResampleContext *s; + AVLFG rnd; + int ret = 0; + uint8_t *in_buf = NULL; + uint8_t *out_buf = NULL; + unsigned int in_buf_size; + unsigned int out_buf_size; + uint8_t *in_data[AVRESAMPLE_MAX_CHANNELS] = { 0 }; + uint8_t *out_data[AVRESAMPLE_MAX_CHANNELS] = { 0 }; + int in_linesize; + int out_linesize; + uint64_t in_ch_layout; + int in_channels; + enum AVSampleFormat in_fmt; + int in_rate; + uint64_t out_ch_layout; + int out_channels; + enum AVSampleFormat out_fmt; + int out_rate; + int num_formats, num_rates, num_layouts; + int i, j, k, l, m, n; + + num_formats = 2; + num_rates = 2; + num_layouts = 2; + if (argc > 1) { + if (!av_strncasecmp(argv[1], "-h", 3)) { + av_log(NULL, AV_LOG_INFO, "Usage: avresample-test [<num formats> " + "[<num sample rates> [<num channel layouts>]]]\n" + "Default is 2 2 2\n"); + return 0; + } + num_formats = strtol(argv[1], NULL, 0); + num_formats = av_clip(num_formats, 1, FF_ARRAY_ELEMS(formats)); + } + if (argc > 2) { + num_rates = strtol(argv[2], NULL, 0); + num_rates = av_clip(num_rates, 1, FF_ARRAY_ELEMS(rates)); + } + if (argc > 3) { + num_layouts = strtol(argv[3], NULL, 0); + num_layouts = av_clip(num_layouts, 1, FF_ARRAY_ELEMS(layouts)); + } + + av_log_set_level(AV_LOG_DEBUG); + + av_lfg_init(&rnd, 0xC0FFEE); + + in_buf_size = av_samples_get_buffer_size(&in_linesize, 8, 48000 * 6, + AV_SAMPLE_FMT_DBLP, 0); + out_buf_size = in_buf_size; + + in_buf = av_malloc(in_buf_size); + if (!in_buf) + goto end; + out_buf = av_malloc(out_buf_size); + if (!out_buf) + goto end; + + s = avresample_alloc_context(); + if (!s) { + av_log(NULL, AV_LOG_ERROR, "Error allocating AVAudioResampleContext\n"); + ret = 1; + goto end; + } + + for (i = 0; i < num_formats; i++) { + in_fmt = formats[i]; + for (k = 0; k < num_layouts; k++) { + in_ch_layout = layouts[k]; + in_channels = av_get_channel_layout_nb_channels(in_ch_layout); + for (m = 0; m < num_rates; m++) { + in_rate = rates[m]; + + ret = av_samples_fill_arrays(in_data, &in_linesize, in_buf, + in_channels, in_rate * 6, + in_fmt, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "failed in_data fill arrays\n"); + goto end; + } + audiogen(&rnd, (void **)in_data, in_fmt, in_channels, in_rate, in_rate * 6); + + for (j = 0; j < num_formats; j++) { + out_fmt = formats[j]; + for (l = 0; l < num_layouts; l++) { + out_ch_layout = layouts[l]; + out_channels = av_get_channel_layout_nb_channels(out_ch_layout); + for (n = 0; n < num_rates; n++) { + out_rate = rates[n]; + + av_log(NULL, AV_LOG_INFO, "%s to %s, %d to %d channels, %d Hz to %d Hz\n", + av_get_sample_fmt_name(in_fmt), av_get_sample_fmt_name(out_fmt), + in_channels, out_channels, in_rate, out_rate); + + ret = av_samples_fill_arrays(out_data, &out_linesize, + out_buf, out_channels, + out_rate * 6, out_fmt, 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "failed out_data fill arrays\n"); + goto end; + } + + av_opt_set_int(s, "in_channel_layout", in_ch_layout, 0); + av_opt_set_int(s, "in_sample_fmt", in_fmt, 0); + av_opt_set_int(s, "in_sample_rate", in_rate, 0); + av_opt_set_int(s, "out_channel_layout", out_ch_layout, 0); + av_opt_set_int(s, "out_sample_fmt", out_fmt, 0); + av_opt_set_int(s, "out_sample_rate", out_rate, 0); + + av_opt_set_int(s, "internal_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); + + ret = avresample_open(s); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Error opening context\n"); + goto end; + } + + ret = avresample_convert(s, out_data, out_linesize, out_rate * 6, + in_data, in_linesize, in_rate * 6); + if (ret < 0) { + char errbuf[256]; + av_strerror(ret, errbuf, sizeof(errbuf)); + av_log(NULL, AV_LOG_ERROR, "%s\n", errbuf); + goto end; + } + av_log(NULL, AV_LOG_INFO, "Converted %d samples to %d samples\n", + in_rate * 6, ret); + if (avresample_get_delay(s) > 0) + av_log(NULL, AV_LOG_INFO, "%d delay samples not converted\n", + avresample_get_delay(s)); + if (avresample_available(s) > 0) + av_log(NULL, AV_LOG_INFO, "%d samples available for output\n", + avresample_available(s)); + av_log(NULL, AV_LOG_INFO, "\n"); + + avresample_close(s); + } + } + } + } + } + } + + ret = 0; + +end: + av_freep(&in_buf); + av_freep(&out_buf); + avresample_free(&s); + return ret; +} diff --git a/lib/ffmpeg/libavresample/avresample.h b/lib/ffmpeg/libavresample/avresample.h new file mode 100644 index 0000000000..d26f2ca223 --- /dev/null +++ b/lib/ffmpeg/libavresample/avresample.h @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_AVRESAMPLE_H +#define AVRESAMPLE_AVRESAMPLE_H + +/** + * @file + * @ingroup lavr + * external API header + */ + +/** + * @defgroup lavr Libavresample + * @{ + * + * Libavresample (lavr) is a library that handles audio resampling, sample + * format conversion and mixing. + * + * Interaction with lavr is done through AVAudioResampleContext, which is + * allocated with avresample_alloc_context(). It is opaque, so all parameters + * must be set with the @ref avoptions API. + * + * For example the following code will setup conversion from planar float sample + * format to interleaved signed 16-bit integer, downsampling from 48kHz to + * 44.1kHz and downmixing from 5.1 channels to stereo (using the default mixing + * matrix): + * @code + * AVAudioResampleContext *avr = avresample_alloc_context(); + * av_opt_set_int(avr, "in_channel_layout", AV_CH_LAYOUT_5POINT1, 0); + * av_opt_set_int(avr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); + * av_opt_set_int(avr, "in_sample_rate", 48000, 0); + * av_opt_set_int(avr, "out_sample_rate", 44100, 0); + * av_opt_set_int(avr, "in_sample_fmt", AV_SAMPLE_FMT_FLTP, 0); + * av_opt_set_int(avr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); + * @endcode + * + * Once the context is initialized, it must be opened with avresample_open(). If + * you need to change the conversion parameters, you must close the context with + * avresample_close(), change the parameters as described above, then reopen it + * again. + * + * The conversion itself is done by repeatedly calling avresample_convert(). + * Note that the samples may get buffered in two places in lavr. The first one + * is the output FIFO, where the samples end up if the output buffer is not + * large enough. The data stored in there may be retrieved at any time with + * avresample_read(). The second place is the resampling delay buffer, + * applicable only when resampling is done. The samples in it require more input + * before they can be processed. Their current amount is returned by + * avresample_get_delay(). At the end of conversion the resampling buffer can be + * flushed by calling avresample_convert() with NULL input. + * + * The following code demonstrates the conversion loop assuming the parameters + * from above and caller-defined functions get_input() and handle_output(): + * @code + * uint8_t **input; + * int in_linesize, in_samples; + * + * while (get_input(&input, &in_linesize, &in_samples)) { + * uint8_t *output + * int out_linesize; + * int out_samples = avresample_available(avr) + + * av_rescale_rnd(avresample_get_delay(avr) + + * in_samples, 44100, 48000, AV_ROUND_UP); + * av_samples_alloc(&output, &out_linesize, 2, out_samples, + * AV_SAMPLE_FMT_S16, 0); + * out_samples = avresample_convert(avr, &output, out_linesize, out_samples, + * input, in_linesize, in_samples); + * handle_output(output, out_linesize, out_samples); + * av_freep(&output); + * } + * @endcode + * + * When the conversion is finished and the FIFOs are flushed if required, the + * conversion context and everything associated with it must be freed with + * avresample_free(). + */ + +#include "libavutil/avutil.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" + +#include "libavresample/version.h" + +#define AVRESAMPLE_MAX_CHANNELS 32 + +typedef struct AVAudioResampleContext AVAudioResampleContext; + +/** Mixing Coefficient Types */ +enum AVMixCoeffType { + AV_MIX_COEFF_TYPE_Q8, /** 16-bit 8.8 fixed-point */ + AV_MIX_COEFF_TYPE_Q15, /** 32-bit 17.15 fixed-point */ + AV_MIX_COEFF_TYPE_FLT, /** floating-point */ + AV_MIX_COEFF_TYPE_NB, /** Number of coeff types. Not part of ABI */ +}; + +/** Resampling Filter Types */ +enum AVResampleFilterType { + AV_RESAMPLE_FILTER_TYPE_CUBIC, /**< Cubic */ + AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL, /**< Blackman Nuttall Windowed Sinc */ + AV_RESAMPLE_FILTER_TYPE_KAISER, /**< Kaiser Windowed Sinc */ +}; + +enum AVResampleDitherMethod { + AV_RESAMPLE_DITHER_NONE, /**< Do not use dithering */ + AV_RESAMPLE_DITHER_RECTANGULAR, /**< Rectangular Dither */ + AV_RESAMPLE_DITHER_TRIANGULAR, /**< Triangular Dither*/ + AV_RESAMPLE_DITHER_TRIANGULAR_HP, /**< Triangular Dither with High Pass */ + AV_RESAMPLE_DITHER_TRIANGULAR_NS, /**< Triangular Dither with Noise Shaping */ + AV_RESAMPLE_DITHER_NB, /**< Number of dither types. Not part of ABI. */ +}; + +/** + * Return the LIBAVRESAMPLE_VERSION_INT constant. + */ +unsigned avresample_version(void); + +/** + * Return the libavresample build-time configuration. + * @return configure string + */ +const char *avresample_configuration(void); + +/** + * Return the libavresample license. + */ +const char *avresample_license(void); + +/** + * Get the AVClass for AVAudioResampleContext. + * + * Can be used in combination with AV_OPT_SEARCH_FAKE_OBJ for examining options + * without allocating a context. + * + * @see av_opt_find(). + * + * @return AVClass for AVAudioResampleContext + */ +const AVClass *avresample_get_class(void); + +/** + * Allocate AVAudioResampleContext and set options. + * + * @return allocated audio resample context, or NULL on failure + */ +AVAudioResampleContext *avresample_alloc_context(void); + +/** + * Initialize AVAudioResampleContext. + * + * @param avr audio resample context + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_open(AVAudioResampleContext *avr); + +/** + * Close AVAudioResampleContext. + * + * This closes the context, but it does not change the parameters. The context + * can be reopened with avresample_open(). It does, however, clear the output + * FIFO and any remaining leftover samples in the resampling delay buffer. If + * there was a custom matrix being used, that is also cleared. + * + * @see avresample_convert() + * @see avresample_set_matrix() + * + * @param avr audio resample context + */ +void avresample_close(AVAudioResampleContext *avr); + +/** + * Free AVAudioResampleContext and associated AVOption values. + * + * This also calls avresample_close() before freeing. + * + * @param avr audio resample context + */ +void avresample_free(AVAudioResampleContext **avr); + +/** + * Generate a channel mixing matrix. + * + * This function is the one used internally by libavresample for building the + * default mixing matrix. It is made public just as a utility function for + * building custom matrices. + * + * @param in_layout input channel layout + * @param out_layout output channel layout + * @param center_mix_level mix level for the center channel + * @param surround_mix_level mix level for the surround channel(s) + * @param lfe_mix_level mix level for the low-frequency effects channel + * @param normalize if 1, coefficients will be normalized to prevent + * overflow. if 0, coefficients will not be + * normalized. + * @param[out] matrix mixing coefficients; matrix[i + stride * o] is + * the weight of input channel i in output channel o. + * @param stride distance between adjacent input channels in the + * matrix array + * @param matrix_encoding matrixed stereo downmix mode (e.g. dplii) + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_build_matrix(uint64_t in_layout, uint64_t out_layout, + double center_mix_level, double surround_mix_level, + double lfe_mix_level, int normalize, double *matrix, + int stride, enum AVMatrixEncoding matrix_encoding); + +/** + * Get the current channel mixing matrix. + * + * If no custom matrix has been previously set or the AVAudioResampleContext is + * not open, an error is returned. + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride); + +/** + * Set channel mixing matrix. + * + * Allows for setting a custom mixing matrix, overriding the default matrix + * generated internally during avresample_open(). This function can be called + * anytime on an allocated context, either before or after calling + * avresample_open(), as long as the channel layouts have been set. + * avresample_convert() always uses the current matrix. + * Calling avresample_close() on the context will clear the current matrix. + * + * @see avresample_close() + * + * @param avr audio resample context + * @param matrix mixing coefficients; matrix[i + stride * o] is the weight of + * input channel i in output channel o. + * @param stride distance between adjacent input channels in the matrix array + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride); + +/** + * Set a customized input channel mapping. + * + * This function can only be called when the allocated context is not open. + * Also, the input channel layout must have already been set. + * + * Calling avresample_close() on the context will clear the channel mapping. + * + * The map for each input channel specifies the channel index in the source to + * use for that particular channel, or -1 to mute the channel. Source channels + * can be duplicated by using the same index for multiple input channels. + * + * Examples: + * + * Reordering 5.1 AAC order (C,L,R,Ls,Rs,LFE) to Libav order (L,R,C,LFE,Ls,Rs): + * { 1, 2, 0, 5, 3, 4 } + * + * Muting the 3rd channel in 4-channel input: + * { 0, 1, -1, 3 } + * + * Duplicating the left channel of stereo input: + * { 0, 0 } + * + * @param avr audio resample context + * @param channel_map customized input channel mapping + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_set_channel_mapping(AVAudioResampleContext *avr, + const int *channel_map); + +/** + * Set compensation for resampling. + * + * This can be called anytime after avresample_open(). If resampling is not + * automatically enabled because of a sample rate conversion, the + * "force_resampling" option must have been set to 1 when opening the context + * in order to use resampling compensation. + * + * @param avr audio resample context + * @param sample_delta compensation delta, in samples + * @param compensation_distance compensation distance, in samples + * @return 0 on success, negative AVERROR code on failure + */ +int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta, + int compensation_distance); + +/** + * Convert input samples and write them to the output FIFO. + * + * The upper bound on the number of output samples is given by + * avresample_available() + (avresample_get_delay() + number of input samples) * + * output sample rate / input sample rate. + * + * The output data can be NULL or have fewer allocated samples than required. + * In this case, any remaining samples not written to the output will be added + * to an internal FIFO buffer, to be returned at the next call to this function + * or to avresample_read(). + * + * If converting sample rate, there may be data remaining in the internal + * resampling delay buffer. avresample_get_delay() tells the number of remaining + * samples. To get this data as output, call avresample_convert() with NULL + * input. + * + * At the end of the conversion process, there may be data remaining in the + * internal FIFO buffer. avresample_available() tells the number of remaining + * samples. To get this data as output, either call avresample_convert() with + * NULL input or call avresample_read(). + * + * @see avresample_available() + * @see avresample_read() + * @see avresample_get_delay() + * + * @param avr audio resample context + * @param output output data pointers + * @param out_plane_size output plane size, in bytes. + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * output, which could slow down some conversions. + * @param out_samples maximum number of samples that the output buffer can hold + * @param input input data pointers + * @param in_plane_size input plane size, in bytes + * This can be 0 if unknown, but that will lead to + * optimized functions not being used directly on the + * input, which could slow down some conversions. + * @param in_samples number of input samples to convert + * @return number of samples written to the output buffer, + * not including converted samples added to the internal + * output FIFO + */ +int avresample_convert(AVAudioResampleContext *avr, uint8_t **output, + int out_plane_size, int out_samples, uint8_t **input, + int in_plane_size, int in_samples); + +/** + * Return the number of samples currently in the resampling delay buffer. + * + * When resampling, there may be a delay between the input and output. Any + * unconverted samples in each call are stored internally in a delay buffer. + * This function allows the user to determine the current number of samples in + * the delay buffer, which can be useful for synchronization. + * + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples currently in the resampling delay buffer + */ +int avresample_get_delay(AVAudioResampleContext *avr); + +/** + * Return the number of available samples in the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. The samples in the FIFO can be read with avresample_read() or + * avresample_convert(). + * + * @see avresample_read() + * @see avresample_convert() + * + * @param avr audio resample context + * @return number of samples available for reading + */ +int avresample_available(AVAudioResampleContext *avr); + +/** + * Read samples from the output FIFO. + * + * During conversion, if the user does not specify an output buffer or + * specifies an output buffer that is smaller than what is needed, remaining + * samples that are not written to the output are stored to an internal FIFO + * buffer. This function can be used to read samples from that internal FIFO. + * + * @see avresample_available() + * @see avresample_convert() + * + * @param avr audio resample context + * @param output output data pointers. May be NULL, in which case + * nb_samples of data is discarded from output FIFO. + * @param nb_samples number of samples to read from the FIFO + * @return the number of samples written to output + */ +int avresample_read(AVAudioResampleContext *avr, uint8_t **output, int nb_samples); + +/** + * @} + */ + +#endif /* AVRESAMPLE_AVRESAMPLE_H */ diff --git a/lib/ffmpeg/libavresample/dither.c b/lib/ffmpeg/libavresample/dither.c new file mode 100644 index 0000000000..f24bf5c765 --- /dev/null +++ b/lib/ffmpeg/libavresample/dither.c @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * Triangular with Noise Shaping is based on opusfile. + * Copyright (c) 1994-2012 by the Xiph.Org Foundation and contributors + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Dithered Audio Sample Quantization + * + * Converts from dbl, flt, or s32 to s16 using dithering. + */ + +#include <math.h> +#include <stdint.h> + +#include "libavutil/common.h" +#include "libavutil/lfg.h" +#include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "audio_convert.h" +#include "dither.h" +#include "internal.h" + +typedef struct DitherState { + int mute; + unsigned int seed; + AVLFG lfg; + float *noise_buf; + int noise_buf_size; + int noise_buf_ptr; + float dither_a[4]; + float dither_b[4]; +} DitherState; + +struct DitherContext { + DitherDSPContext ddsp; + enum AVResampleDitherMethod method; + int apply_map; + ChannelMapInfo *ch_map_info; + + int mute_dither_threshold; // threshold for disabling dither + int mute_reset_threshold; // threshold for resetting noise shaping + const float *ns_coef_b; // noise shaping coeffs + const float *ns_coef_a; // noise shaping coeffs + + int channels; + DitherState *state; // dither states for each channel + + AudioData *flt_data; // input data in fltp + AudioData *s16_data; // dithered output in s16p + AudioConvert *ac_in; // converter for input to fltp + AudioConvert *ac_out; // converter for s16p to s16 (if needed) + + void (*quantize)(int16_t *dst, const float *src, float *dither, int len); + int samples_align; +}; + +/* mute threshold, in seconds */ +#define MUTE_THRESHOLD_SEC 0.000333 + +/* scale factor for 16-bit output. + The signal is attenuated slightly to avoid clipping */ +#define S16_SCALE 32753.0f + +/* scale to convert lfg from INT_MIN/INT_MAX to -0.5/0.5 */ +#define LFG_SCALE (1.0f / (2.0f * INT32_MAX)) + +/* noise shaping coefficients */ + +static const float ns_48_coef_b[4] = { + 2.2374f, -0.7339f, -0.1251f, -0.6033f +}; + +static const float ns_48_coef_a[4] = { + 0.9030f, 0.0116f, -0.5853f, -0.2571f +}; + +static const float ns_44_coef_b[4] = { + 2.2061f, -0.4707f, -0.2534f, -0.6213f +}; + +static const float ns_44_coef_a[4] = { + 1.0587f, 0.0676f, -0.6054f, -0.2738f +}; + +static void dither_int_to_float_rectangular_c(float *dst, int *src, int len) +{ + int i; + for (i = 0; i < len; i++) + dst[i] = src[i] * LFG_SCALE; +} + +static void dither_int_to_float_triangular_c(float *dst, int *src0, int len) +{ + int i; + int *src1 = src0 + len; + + for (i = 0; i < len; i++) { + float r = src0[i] * LFG_SCALE; + r += src1[i] * LFG_SCALE; + dst[i] = r; + } +} + +static void quantize_c(int16_t *dst, const float *src, float *dither, int len) +{ + int i; + for (i = 0; i < len; i++) + dst[i] = av_clip_int16(lrintf(src[i] * S16_SCALE + dither[i])); +} + +#define SQRT_1_6 0.40824829046386301723f + +static void dither_highpass_filter(float *src, int len) +{ + int i; + + /* filter is from libswresample in FFmpeg */ + for (i = 0; i < len - 2; i++) + src[i] = (-src[i] + 2 * src[i + 1] - src[i + 2]) * SQRT_1_6; +} + +static int generate_dither_noise(DitherContext *c, DitherState *state, + int min_samples) +{ + int i; + int nb_samples = FFALIGN(min_samples, 16) + 16; + int buf_samples = nb_samples * + (c->method == AV_RESAMPLE_DITHER_RECTANGULAR ? 1 : 2); + unsigned int *noise_buf_ui; + + av_freep(&state->noise_buf); + state->noise_buf_size = state->noise_buf_ptr = 0; + + state->noise_buf = av_malloc(buf_samples * sizeof(*state->noise_buf)); + if (!state->noise_buf) + return AVERROR(ENOMEM); + state->noise_buf_size = FFALIGN(min_samples, 16); + noise_buf_ui = (unsigned int *)state->noise_buf; + + av_lfg_init(&state->lfg, state->seed); + for (i = 0; i < buf_samples; i++) + noise_buf_ui[i] = av_lfg_get(&state->lfg); + + c->ddsp.dither_int_to_float(state->noise_buf, noise_buf_ui, nb_samples); + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_HP) + dither_highpass_filter(state->noise_buf, nb_samples); + + return 0; +} + +static void quantize_triangular_ns(DitherContext *c, DitherState *state, + int16_t *dst, const float *src, + int nb_samples) +{ + int i, j; + float *dither = &state->noise_buf[state->noise_buf_ptr]; + + if (state->mute > c->mute_reset_threshold) + memset(state->dither_a, 0, sizeof(state->dither_a)); + + for (i = 0; i < nb_samples; i++) { + float err = 0; + float sample = src[i] * S16_SCALE; + + for (j = 0; j < 4; j++) { + err += c->ns_coef_b[j] * state->dither_b[j] - + c->ns_coef_a[j] * state->dither_a[j]; + } + for (j = 3; j > 0; j--) { + state->dither_a[j] = state->dither_a[j - 1]; + state->dither_b[j] = state->dither_b[j - 1]; + } + state->dither_a[0] = err; + sample -= err; + + if (state->mute > c->mute_dither_threshold) { + dst[i] = av_clip_int16(lrintf(sample)); + state->dither_b[0] = 0; + } else { + dst[i] = av_clip_int16(lrintf(sample + dither[i])); + state->dither_b[0] = av_clipf(dst[i] - sample, -1.5f, 1.5f); + } + + state->mute++; + if (src[i]) + state->mute = 0; + } +} + +static int convert_samples(DitherContext *c, int16_t **dst, float * const *src, + int channels, int nb_samples) +{ + int ch, ret; + int aligned_samples = FFALIGN(nb_samples, 16); + + for (ch = 0; ch < channels; ch++) { + DitherState *state = &c->state[ch]; + + if (state->noise_buf_size < aligned_samples) { + ret = generate_dither_noise(c, state, nb_samples); + if (ret < 0) + return ret; + } else if (state->noise_buf_size - state->noise_buf_ptr < aligned_samples) { + state->noise_buf_ptr = 0; + } + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + quantize_triangular_ns(c, state, dst[ch], src[ch], nb_samples); + } else { + c->quantize(dst[ch], src[ch], + &state->noise_buf[state->noise_buf_ptr], + FFALIGN(nb_samples, c->samples_align)); + } + + state->noise_buf_ptr += aligned_samples; + } + + return 0; +} + +int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src) +{ + int ret; + AudioData *flt_data; + + /* output directly to dst if it is planar */ + if (dst->sample_fmt == AV_SAMPLE_FMT_S16P) + c->s16_data = dst; + else { + /* make sure s16_data is large enough for the output */ + ret = ff_audio_data_realloc(c->s16_data, src->nb_samples); + if (ret < 0) + return ret; + } + + if (src->sample_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) { + /* make sure flt_data is large enough for the input */ + ret = ff_audio_data_realloc(c->flt_data, src->nb_samples); + if (ret < 0) + return ret; + flt_data = c->flt_data; + } + + if (src->sample_fmt != AV_SAMPLE_FMT_FLTP) { + /* convert input samples to fltp and scale to s16 range */ + ret = ff_audio_convert(c->ac_in, flt_data, src); + if (ret < 0) + return ret; + } else if (c->apply_map) { + ret = ff_audio_data_copy(flt_data, src, c->ch_map_info); + if (ret < 0) + return ret; + } else { + flt_data = src; + } + + /* check alignment and padding constraints */ + if (c->method != AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + int ptr_align = FFMIN(flt_data->ptr_align, c->s16_data->ptr_align); + int samples_align = FFMIN(flt_data->samples_align, c->s16_data->samples_align); + int aligned_len = FFALIGN(src->nb_samples, c->ddsp.samples_align); + + if (!(ptr_align % c->ddsp.ptr_align) && samples_align >= aligned_len) { + c->quantize = c->ddsp.quantize; + c->samples_align = c->ddsp.samples_align; + } else { + c->quantize = quantize_c; + c->samples_align = 1; + } + } + + ret = convert_samples(c, (int16_t **)c->s16_data->data, + (float * const *)flt_data->data, src->channels, + src->nb_samples); + if (ret < 0) + return ret; + + c->s16_data->nb_samples = src->nb_samples; + + /* interleave output to dst if needed */ + if (dst->sample_fmt == AV_SAMPLE_FMT_S16) { + ret = ff_audio_convert(c->ac_out, dst, c->s16_data); + if (ret < 0) + return ret; + } else + c->s16_data = NULL; + + return 0; +} + +void ff_dither_free(DitherContext **cp) +{ + DitherContext *c = *cp; + int ch; + + if (!c) + return; + ff_audio_data_free(&c->flt_data); + ff_audio_data_free(&c->s16_data); + ff_audio_convert_free(&c->ac_in); + ff_audio_convert_free(&c->ac_out); + for (ch = 0; ch < c->channels; ch++) + av_free(c->state[ch].noise_buf); + av_free(c->state); + av_freep(cp); +} + +static void dither_init(DitherDSPContext *ddsp, + enum AVResampleDitherMethod method) +{ + ddsp->quantize = quantize_c; + ddsp->ptr_align = 1; + ddsp->samples_align = 1; + + if (method == AV_RESAMPLE_DITHER_RECTANGULAR) + ddsp->dither_int_to_float = dither_int_to_float_rectangular_c; + else + ddsp->dither_int_to_float = dither_int_to_float_triangular_c; + + if (ARCH_X86) + ff_dither_init_x86(ddsp, method); +} + +DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate, int apply_map) +{ + AVLFG seed_gen; + DitherContext *c; + int ch; + + if (av_get_packed_sample_fmt(out_fmt) != AV_SAMPLE_FMT_S16 || + av_get_bytes_per_sample(in_fmt) <= 2) { + av_log(avr, AV_LOG_ERROR, "dithering %s to %s is not supported\n", + av_get_sample_fmt_name(in_fmt), av_get_sample_fmt_name(out_fmt)); + return NULL; + } + + c = av_mallocz(sizeof(*c)); + if (!c) + return NULL; + + c->apply_map = apply_map; + if (apply_map) + c->ch_map_info = &avr->ch_map_info; + + if (avr->dither_method == AV_RESAMPLE_DITHER_TRIANGULAR_NS && + sample_rate != 48000 && sample_rate != 44100) { + av_log(avr, AV_LOG_WARNING, "sample rate must be 48000 or 44100 Hz " + "for triangular_ns dither. using triangular_hp instead.\n"); + avr->dither_method = AV_RESAMPLE_DITHER_TRIANGULAR_HP; + } + c->method = avr->dither_method; + dither_init(&c->ddsp, c->method); + + if (c->method == AV_RESAMPLE_DITHER_TRIANGULAR_NS) { + if (sample_rate == 48000) { + c->ns_coef_b = ns_48_coef_b; + c->ns_coef_a = ns_48_coef_a; + } else { + c->ns_coef_b = ns_44_coef_b; + c->ns_coef_a = ns_44_coef_a; + } + } + + /* Either s16 or s16p output format is allowed, but s16p is used + internally, so we need to use a temp buffer and interleave if the output + format is s16 */ + if (out_fmt != AV_SAMPLE_FMT_S16P) { + c->s16_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_S16P, + "dither s16 buffer"); + if (!c->s16_data) + goto fail; + + c->ac_out = ff_audio_convert_alloc(avr, out_fmt, AV_SAMPLE_FMT_S16P, + channels, sample_rate, 0); + if (!c->ac_out) + goto fail; + } + + if (in_fmt != AV_SAMPLE_FMT_FLTP || c->apply_map) { + c->flt_data = ff_audio_data_alloc(channels, 1024, AV_SAMPLE_FMT_FLTP, + "dither flt buffer"); + if (!c->flt_data) + goto fail; + } + if (in_fmt != AV_SAMPLE_FMT_FLTP) { + c->ac_in = ff_audio_convert_alloc(avr, AV_SAMPLE_FMT_FLTP, in_fmt, + channels, sample_rate, c->apply_map); + if (!c->ac_in) + goto fail; + } + + c->state = av_mallocz(channels * sizeof(*c->state)); + if (!c->state) + goto fail; + c->channels = channels; + + /* calculate thresholds for turning off dithering during periods of + silence to avoid replacing digital silence with quiet dither noise */ + c->mute_dither_threshold = lrintf(sample_rate * MUTE_THRESHOLD_SEC); + c->mute_reset_threshold = c->mute_dither_threshold * 4; + + /* initialize dither states */ + av_lfg_init(&seed_gen, 0xC0FFEE); + for (ch = 0; ch < channels; ch++) { + DitherState *state = &c->state[ch]; + state->mute = c->mute_reset_threshold + 1; + state->seed = av_lfg_get(&seed_gen); + generate_dither_noise(c, state, FFMAX(32768, sample_rate / 2)); + } + + return c; + +fail: + ff_dither_free(&c); + return NULL; +} diff --git a/lib/ffmpeg/libavresample/dither.h b/lib/ffmpeg/libavresample/dither.h new file mode 100644 index 0000000000..8db37146fa --- /dev/null +++ b/lib/ffmpeg/libavresample/dither.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_DITHER_H +#define AVRESAMPLE_DITHER_H + +#include "avresample.h" +#include "audio_data.h" + +typedef struct DitherContext DitherContext; + +typedef struct DitherDSPContext { + /** + * Convert samples from flt to s16 with added dither noise. + * + * @param dst destination float array, range -0.5 to 0.5 + * @param src source int array, range INT_MIN to INT_MAX. + * @param dither float dither noise array + * @param len number of samples + */ + void (*quantize)(int16_t *dst, const float *src, float *dither, int len); + + int ptr_align; ///< src and dst constraits for quantize() + int samples_align; ///< len constraits for quantize() + + /** + * Convert dither noise from int to float with triangular distribution. + * + * @param dst destination float array, range -0.5 to 0.5 + * constraints: 32-byte aligned + * @param src0 source int array, range INT_MIN to INT_MAX. + * the array size is len * 2 + * constraints: 32-byte aligned + * @param len number of output noise samples + * constraints: multiple of 16 + */ + void (*dither_int_to_float)(float *dst, int *src0, int len); +} DitherDSPContext; + +/** + * Allocate and initialize a DitherContext. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * DitherContext. + * + * @param avr AVAudioResampleContext + * @return newly-allocated DitherContext + */ +DitherContext *ff_dither_alloc(AVAudioResampleContext *avr, + enum AVSampleFormat out_fmt, + enum AVSampleFormat in_fmt, + int channels, int sample_rate, int apply_map); + +/** + * Free a DitherContext. + * + * @param c DitherContext + */ +void ff_dither_free(DitherContext **c); + +/** + * Convert audio sample format with dithering. + * + * @param c DitherContext + * @param dst destination audio data + * @param src source audio data + * @return 0 if ok, negative AVERROR code on failure + */ +int ff_convert_dither(DitherContext *c, AudioData *dst, AudioData *src); + +/* arch-specific initialization functions */ + +void ff_dither_init_x86(DitherDSPContext *ddsp, + enum AVResampleDitherMethod method); + +#endif /* AVRESAMPLE_DITHER_H */ diff --git a/lib/ffmpeg/libavresample/internal.h b/lib/ffmpeg/libavresample/internal.h new file mode 100644 index 0000000000..057f89a49c --- /dev/null +++ b/lib/ffmpeg/libavresample/internal.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_INTERNAL_H +#define AVRESAMPLE_INTERNAL_H + +#include "libavutil/audio_fifo.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" +#include "avresample.h" + +typedef struct AudioData AudioData; +typedef struct AudioConvert AudioConvert; +typedef struct AudioMix AudioMix; +typedef struct ResampleContext ResampleContext; + +enum RemapPoint { + REMAP_NONE, + REMAP_IN_COPY, + REMAP_IN_CONVERT, + REMAP_OUT_COPY, + REMAP_OUT_CONVERT, +}; + +typedef struct ChannelMapInfo { + int channel_map[AVRESAMPLE_MAX_CHANNELS]; /**< source index of each output channel, -1 if not remapped */ + int do_remap; /**< remap needed */ + int channel_copy[AVRESAMPLE_MAX_CHANNELS]; /**< dest index to copy from */ + int do_copy; /**< copy needed */ + int channel_zero[AVRESAMPLE_MAX_CHANNELS]; /**< dest index to zero */ + int do_zero; /**< zeroing needed */ + int input_map[AVRESAMPLE_MAX_CHANNELS]; /**< dest index of each input channel */ +} ChannelMapInfo; + +struct AVAudioResampleContext { + const AVClass *av_class; /**< AVClass for logging and AVOptions */ + + uint64_t in_channel_layout; /**< input channel layout */ + enum AVSampleFormat in_sample_fmt; /**< input sample format */ + int in_sample_rate; /**< input sample rate */ + uint64_t out_channel_layout; /**< output channel layout */ + enum AVSampleFormat out_sample_fmt; /**< output sample format */ + int out_sample_rate; /**< output sample rate */ + enum AVSampleFormat internal_sample_fmt; /**< internal sample format */ + enum AVMixCoeffType mix_coeff_type; /**< mixing coefficient type */ + double center_mix_level; /**< center mix level */ + double surround_mix_level; /**< surround mix level */ + double lfe_mix_level; /**< lfe mix level */ + int normalize_mix_level; /**< enable mix level normalization */ + int force_resampling; /**< force resampling */ + int filter_size; /**< length of each FIR filter in the resampling filterbank relative to the cutoff frequency */ + int phase_shift; /**< log2 of the number of entries in the resampling polyphase filterbank */ + int linear_interp; /**< if 1 then the resampling FIR filter will be linearly interpolated */ + double cutoff; /**< resampling cutoff frequency. 1.0 corresponds to half the output sample rate */ + enum AVResampleFilterType filter_type; /**< resampling filter type */ + int kaiser_beta; /**< beta value for Kaiser window (only applicable if filter_type == AV_FILTER_TYPE_KAISER) */ + enum AVResampleDitherMethod dither_method; /**< dither method */ + + int in_channels; /**< number of input channels */ + int out_channels; /**< number of output channels */ + int resample_channels; /**< number of channels used for resampling */ + int downmix_needed; /**< downmixing is needed */ + int upmix_needed; /**< upmixing is needed */ + int mixing_needed; /**< either upmixing or downmixing is needed */ + int resample_needed; /**< resampling is needed */ + int in_convert_needed; /**< input sample format conversion is needed */ + int out_convert_needed; /**< output sample format conversion is needed */ + int in_copy_needed; /**< input data copy is needed */ + + AudioData *in_buffer; /**< buffer for converted input */ + AudioData *resample_out_buffer; /**< buffer for output from resampler */ + AudioData *out_buffer; /**< buffer for converted output */ + AVAudioFifo *out_fifo; /**< FIFO for output samples */ + + AudioConvert *ac_in; /**< input sample format conversion context */ + AudioConvert *ac_out; /**< output sample format conversion context */ + ResampleContext *resample; /**< resampling context */ + AudioMix *am; /**< channel mixing context */ + enum AVMatrixEncoding matrix_encoding; /**< matrixed stereo encoding */ + + /** + * mix matrix + * only used if avresample_set_matrix() is called before avresample_open() + */ + double *mix_matrix; + + int use_channel_map; + enum RemapPoint remap_point; + ChannelMapInfo ch_map_info; +}; + +#endif /* AVRESAMPLE_INTERNAL_H */ diff --git a/lib/ffmpeg/libavresample/libavresample.v b/lib/ffmpeg/libavresample/libavresample.v new file mode 100644 index 0000000000..b8c7c7d2e5 --- /dev/null +++ b/lib/ffmpeg/libavresample/libavresample.v @@ -0,0 +1,4 @@ +LIBAVRESAMPLE_$MAJOR { + global: av*; + local: *; +}; diff --git a/lib/ffmpeg/libavresample/options.c b/lib/ffmpeg/libavresample/options.c new file mode 100644 index 0000000000..39c415b8b8 --- /dev/null +++ b/lib/ffmpeg/libavresample/options.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/mathematics.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "avresample.h" +#include "internal.h" +#include "audio_mix.h" + +/** + * @file + * Options definition for AVAudioResampleContext. + */ + +#define OFFSET(x) offsetof(AVAudioResampleContext, x) +#define PARAM AV_OPT_FLAG_AUDIO_PARAM + +static const AVOption options[] = { + { "in_channel_layout", "Input Channel Layout", OFFSET(in_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM }, + { "in_sample_fmt", "Input Sample Format", OFFSET(in_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, + { "in_sample_rate", "Input Sample Rate", OFFSET(in_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM }, + { "out_channel_layout", "Output Channel Layout", OFFSET(out_channel_layout), AV_OPT_TYPE_INT64, { .i64 = 0 }, INT64_MIN, INT64_MAX, PARAM }, + { "out_sample_fmt", "Output Sample Format", OFFSET(out_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_S16 }, AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NB-1, PARAM }, + { "out_sample_rate", "Output Sample Rate", OFFSET(out_sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1, INT_MAX, PARAM }, + { "internal_sample_fmt", "Internal Sample Format", OFFSET(internal_sample_fmt), AV_OPT_TYPE_INT, { .i64 = AV_SAMPLE_FMT_NONE }, AV_SAMPLE_FMT_NONE, AV_SAMPLE_FMT_NB-1, PARAM, "internal_sample_fmt" }, + {"u8" , "8-bit unsigned integer", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_U8 }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"s16", "16-bit signed integer", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_S16 }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"s32", "32-bit signed integer", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_S32 }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"flt", "32-bit float", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_FLT }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"dbl", "64-bit double", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_DBL }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"u8p" , "8-bit unsigned integer planar", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_U8P }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"s16p", "16-bit signed integer planar", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_S16P }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"s32p", "32-bit signed integer planar", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_S32P }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"fltp", "32-bit float planar", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_FLTP }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + {"dblp", "64-bit double planar", 0, AV_OPT_TYPE_CONST, {.i64 = AV_SAMPLE_FMT_DBLP }, INT_MIN, INT_MAX, PARAM, "internal_sample_fmt"}, + { "mix_coeff_type", "Mixing Coefficient Type", OFFSET(mix_coeff_type), AV_OPT_TYPE_INT, { .i64 = AV_MIX_COEFF_TYPE_FLT }, AV_MIX_COEFF_TYPE_Q8, AV_MIX_COEFF_TYPE_NB-1, PARAM, "mix_coeff_type" }, + { "q8", "16-bit 8.8 Fixed-Point", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MIX_COEFF_TYPE_Q8 }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "q15", "32-bit 17.15 Fixed-Point", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MIX_COEFF_TYPE_Q15 }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "flt", "Floating-Point", 0, AV_OPT_TYPE_CONST, { .i64 = AV_MIX_COEFF_TYPE_FLT }, INT_MIN, INT_MAX, PARAM, "mix_coeff_type" }, + { "center_mix_level", "Center Mix Level", OFFSET(center_mix_level), AV_OPT_TYPE_DOUBLE, { .dbl = M_SQRT1_2 }, -32.0, 32.0, PARAM }, + { "surround_mix_level", "Surround Mix Level", OFFSET(surround_mix_level), AV_OPT_TYPE_DOUBLE, { .dbl = M_SQRT1_2 }, -32.0, 32.0, PARAM }, + { "lfe_mix_level", "LFE Mix Level", OFFSET(lfe_mix_level), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, -32.0, 32.0, PARAM }, + { "normalize_mix_level", "Normalize Mix Level", OFFSET(normalize_mix_level), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, PARAM }, + { "force_resampling", "Force Resampling", OFFSET(force_resampling), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, PARAM }, + { "filter_size", "Resampling Filter Size", OFFSET(filter_size), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 32, /* ??? */ 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.0, 1.0, PARAM }, + /* duplicate option in order to work with avconv */ + { "resample_cutoff", "Cutoff Frequency Ratio", OFFSET(cutoff), AV_OPT_TYPE_DOUBLE, { .dbl = 0.8 }, 0.0, 1.0, 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 = AV_RESAMPLE_FILTER_TYPE_KAISER }, AV_RESAMPLE_FILTER_TYPE_CUBIC, AV_RESAMPLE_FILTER_TYPE_KAISER, PARAM, "filter_type" }, + { "cubic", "Cubic", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_FILTER_TYPE_CUBIC }, INT_MIN, INT_MAX, PARAM, "filter_type" }, + { "blackman_nuttall", "Blackman Nuttall Windowed Sinc", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_FILTER_TYPE_BLACKMAN_NUTTALL }, INT_MIN, INT_MAX, PARAM, "filter_type" }, + { "kaiser", "Kaiser Windowed Sinc", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_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 }, + { "dither_method", "Dither Method", OFFSET(dither_method), AV_OPT_TYPE_INT, { .i64 = AV_RESAMPLE_DITHER_NONE }, 0, AV_RESAMPLE_DITHER_NB-1, PARAM, "dither_method"}, + {"none", "No Dithering", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_NONE }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"rectangular", "Rectangular Dither", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_RECTANGULAR }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular", "Triangular Dither", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular_hp", "Triangular Dither With High Pass", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR_HP }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + {"triangular_ns", "Triangular Dither With Noise Shaping", 0, AV_OPT_TYPE_CONST, { .i64 = AV_RESAMPLE_DITHER_TRIANGULAR_NS }, INT_MIN, INT_MAX, PARAM, "dither_method"}, + { NULL }, +}; + +static const AVClass av_resample_context_class = { + .class_name = "AVAudioResampleContext", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVAudioResampleContext *avresample_alloc_context(void) +{ + AVAudioResampleContext *avr; + + avr = av_mallocz(sizeof(*avr)); + if (!avr) + return NULL; + + avr->av_class = &av_resample_context_class; + av_opt_set_defaults(avr); + + return avr; +} + +const AVClass *avresample_get_class(void) +{ + return &av_resample_context_class; +} diff --git a/lib/ffmpeg/libavresample/resample.c b/lib/ffmpeg/libavresample/resample.c new file mode 100644 index 0000000000..69c9bab893 --- /dev/null +++ b/lib/ffmpeg/libavresample/resample.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" +#include "libavutil/libm.h" +#include "libavutil/log.h" +#include "internal.h" +#include "resample.h" +#include "audio_data.h" + +struct ResampleContext { + AVAudioResampleContext *avr; + AudioData *buffer; + uint8_t *filter_bank; + int filter_length; + int ideal_dst_incr; + int dst_incr; + int index; + int frac; + int src_incr; + int compensation_distance; + int phase_shift; + int phase_mask; + int linear; + enum AVResampleFilterType filter_type; + int kaiser_beta; + double factor; + void (*set_filter)(void *filter, double *tab, int phase, int tap_count); + void (*resample_one)(struct ResampleContext *c, int no_filter, void *dst0, + int dst_index, const void *src0, int src_size, + int index, int frac); +}; + + +/* double template */ +#define CONFIG_RESAMPLE_DBL +#include "resample_template.c" +#undef CONFIG_RESAMPLE_DBL + +/* float template */ +#define CONFIG_RESAMPLE_FLT +#include "resample_template.c" +#undef CONFIG_RESAMPLE_FLT + +/* s32 template */ +#define CONFIG_RESAMPLE_S32 +#include "resample_template.c" +#undef CONFIG_RESAMPLE_S32 + +/* s16 template */ +#include "resample_template.c" + + +/* 0th order modified bessel function of the first kind. */ +static double bessel(double x) +{ + double v = 1; + double lastv = 0; + double t = 1; + int i; + + x = x * x / 4; + for (i = 1; v != lastv; i++) { + lastv = v; + t *= x / (i * i); + v += t; + } + return v; +} + +/* Build a polyphase filterbank. */ +static int build_filter(ResampleContext *c) +{ + int ph, i; + double x, y, w, factor; + double *tab; + int tap_count = c->filter_length; + int phase_count = 1 << c->phase_shift; + const int center = (tap_count - 1) / 2; + + tab = av_malloc(tap_count * sizeof(*tab)); + if (!tab) + return AVERROR(ENOMEM); + + /* if upsampling, only need to interpolate, no filter */ + factor = FFMIN(c->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 (c->filter_type) { + case AV_RESAMPLE_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 AV_RESAMPLE_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 AV_RESAMPLE_FILTER_TYPE_KAISER: + w = 2.0 * x / (factor * tap_count * M_PI); + y *= bessel(c->kaiser_beta * sqrt(FFMAX(1 - w * w, 0))); + break; + } + + tab[i] = y; + norm += y; + } + /* normalize so that an uniform color remains the same */ + for (i = 0; i < tap_count; i++) + tab[i] = tab[i] / norm; + + c->set_filter(c->filter_bank, tab, ph, tap_count); + } + + av_free(tab); + return 0; +} + +ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr) +{ + ResampleContext *c; + int out_rate = avr->out_sample_rate; + int in_rate = avr->in_sample_rate; + double factor = FFMIN(out_rate * avr->cutoff / in_rate, 1.0); + int phase_count = 1 << avr->phase_shift; + int felem_size; + + if (avr->internal_sample_fmt != AV_SAMPLE_FMT_S16P && + avr->internal_sample_fmt != AV_SAMPLE_FMT_S32P && + avr->internal_sample_fmt != AV_SAMPLE_FMT_FLTP && + avr->internal_sample_fmt != AV_SAMPLE_FMT_DBLP) { + av_log(avr, AV_LOG_ERROR, "Unsupported internal format for " + "resampling: %s\n", + av_get_sample_fmt_name(avr->internal_sample_fmt)); + return NULL; + } + c = av_mallocz(sizeof(*c)); + if (!c) + return NULL; + + c->avr = avr; + c->phase_shift = avr->phase_shift; + c->phase_mask = phase_count - 1; + c->linear = avr->linear_interp; + c->factor = factor; + c->filter_length = FFMAX((int)ceil(avr->filter_size / factor), 1); + c->filter_type = avr->filter_type; + c->kaiser_beta = avr->kaiser_beta; + + switch (avr->internal_sample_fmt) { + case AV_SAMPLE_FMT_DBLP: + c->resample_one = resample_one_dbl; + c->set_filter = set_filter_dbl; + break; + case AV_SAMPLE_FMT_FLTP: + c->resample_one = resample_one_flt; + c->set_filter = set_filter_flt; + break; + case AV_SAMPLE_FMT_S32P: + c->resample_one = resample_one_s32; + c->set_filter = set_filter_s32; + break; + case AV_SAMPLE_FMT_S16P: + c->resample_one = resample_one_s16; + c->set_filter = set_filter_s16; + break; + } + + felem_size = av_get_bytes_per_sample(avr->internal_sample_fmt); + c->filter_bank = av_mallocz(c->filter_length * (phase_count + 1) * felem_size); + if (!c->filter_bank) + goto error; + + if (build_filter(c) < 0) + goto error; + + memcpy(&c->filter_bank[(c->filter_length * phase_count + 1) * felem_size], + c->filter_bank, (c->filter_length - 1) * felem_size); + memcpy(&c->filter_bank[c->filter_length * phase_count * felem_size], + &c->filter_bank[(c->filter_length - 1) * felem_size], 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; + + /* allocate internal buffer */ + c->buffer = ff_audio_data_alloc(avr->resample_channels, 0, + avr->internal_sample_fmt, + "resample buffer"); + if (!c->buffer) + goto error; + + av_log(avr, AV_LOG_DEBUG, "resample: %s from %d Hz to %d Hz\n", + av_get_sample_fmt_name(avr->internal_sample_fmt), + avr->in_sample_rate, avr->out_sample_rate); + + return c; + +error: + ff_audio_data_free(&c->buffer); + av_free(c->filter_bank); + av_free(c); + return NULL; +} + +void ff_audio_resample_free(ResampleContext **c) +{ + if (!*c) + return; + ff_audio_data_free(&(*c)->buffer); + av_free((*c)->filter_bank); + av_freep(c); +} + +int avresample_set_compensation(AVAudioResampleContext *avr, int sample_delta, + int compensation_distance) +{ + ResampleContext *c; + AudioData *fifo_buf = NULL; + int ret = 0; + + if (compensation_distance < 0) + return AVERROR(EINVAL); + if (!compensation_distance && sample_delta) + return AVERROR(EINVAL); + + if (!avr->resample_needed) { +#if FF_API_RESAMPLE_CLOSE_OPEN + /* if resampling was not enabled previously, re-initialize the + AVAudioResampleContext and force resampling */ + int fifo_samples; + int restore_matrix = 0; + double matrix[AVRESAMPLE_MAX_CHANNELS * AVRESAMPLE_MAX_CHANNELS] = { 0 }; + + /* buffer any remaining samples in the output FIFO before closing */ + fifo_samples = av_audio_fifo_size(avr->out_fifo); + if (fifo_samples > 0) { + fifo_buf = ff_audio_data_alloc(avr->out_channels, fifo_samples, + avr->out_sample_fmt, NULL); + if (!fifo_buf) + return AVERROR(EINVAL); + ret = ff_audio_data_read_from_fifo(avr->out_fifo, fifo_buf, + fifo_samples); + if (ret < 0) + goto reinit_fail; + } + /* save the channel mixing matrix */ + if (avr->am) { + ret = avresample_get_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS); + if (ret < 0) + goto reinit_fail; + restore_matrix = 1; + } + + /* close the AVAudioResampleContext */ + avresample_close(avr); + + avr->force_resampling = 1; + + /* restore the channel mixing matrix */ + if (restore_matrix) { + ret = avresample_set_matrix(avr, matrix, AVRESAMPLE_MAX_CHANNELS); + if (ret < 0) + goto reinit_fail; + } + + /* re-open the AVAudioResampleContext */ + ret = avresample_open(avr); + if (ret < 0) + goto reinit_fail; + + /* restore buffered samples to the output FIFO */ + if (fifo_samples > 0) { + ret = ff_audio_data_add_to_fifo(avr->out_fifo, fifo_buf, 0, + fifo_samples); + if (ret < 0) + goto reinit_fail; + ff_audio_data_free(&fifo_buf); + } +#else + av_log(avr, AV_LOG_ERROR, "Unable to set resampling compensation\n"); + return AVERROR(EINVAL); +#endif + } + c = avr->resample; + c->compensation_distance = compensation_distance; + if (compensation_distance) { + c->dst_incr = c->ideal_dst_incr - c->ideal_dst_incr * + (int64_t)sample_delta / compensation_distance; + } else { + c->dst_incr = c->ideal_dst_incr; + } + return 0; + +reinit_fail: + ff_audio_data_free(&fifo_buf); + return ret; +} + +static int resample(ResampleContext *c, void *dst, const void *src, + int *consumed, int src_size, int dst_size, int update_ctx) +{ + int dst_index; + int index = c->index; + int frac = c->frac; + int dst_incr_frac = c->dst_incr % c->src_incr; + int dst_incr = c->dst_incr / c->src_incr; + int compensation_distance = c->compensation_distance; + + if (!dst != !src) + return AVERROR(EINVAL); + + if (compensation_distance == 0 && c->filter_length == 1 && + c->phase_shift == 0) { + int64_t index2 = ((int64_t)index) << 32; + int64_t incr = (1LL << 32) * c->dst_incr / c->src_incr; + dst_size = FFMIN(dst_size, + (src_size-1-index) * (int64_t)c->src_incr / + c->dst_incr); + + if (dst) { + for(dst_index = 0; dst_index < dst_size; dst_index++) { + c->resample_one(c, 1, dst, dst_index, src, 0, index2 >> 32, 0); + index2 += incr; + } + } else { + dst_index = dst_size; + } + index += dst_index * dst_incr; + index += (frac + dst_index * (int64_t)dst_incr_frac) / c->src_incr; + frac = (frac + dst_index * (int64_t)dst_incr_frac) % c->src_incr; + } else { + for (dst_index = 0; dst_index < dst_size; dst_index++) { + int sample_index = index >> c->phase_shift; + + if (sample_index + c->filter_length > src_size || + -sample_index >= src_size) + break; + + if (dst) + c->resample_one(c, 0, dst, dst_index, src, src_size, index, frac); + + frac += dst_incr_frac; + index += dst_incr; + if (frac >= c->src_incr) { + frac -= c->src_incr; + index++; + } + if (dst_index + 1 == compensation_distance) { + compensation_distance = 0; + dst_incr_frac = c->ideal_dst_incr % c->src_incr; + dst_incr = c->ideal_dst_incr / c->src_incr; + } + } + } + if (consumed) + *consumed = FFMAX(index, 0) >> c->phase_shift; + + if (update_ctx) { + if (index >= 0) + index &= c->phase_mask; + + if (compensation_distance) { + compensation_distance -= dst_index; + if (compensation_distance <= 0) + return AVERROR_BUG; + } + c->frac = frac; + c->index = index; + c->dst_incr = dst_incr_frac + c->src_incr*dst_incr; + c->compensation_distance = compensation_distance; + } + + return dst_index; +} + +int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src) +{ + int ch, in_samples, in_leftover, consumed = 0, out_samples = 0; + int ret = AVERROR(EINVAL); + + in_samples = src ? src->nb_samples : 0; + in_leftover = c->buffer->nb_samples; + + /* add input samples to the internal buffer */ + if (src) { + ret = ff_audio_data_combine(c->buffer, in_leftover, src, 0, in_samples); + if (ret < 0) + return ret; + } else if (!in_leftover) { + /* no remaining samples to flush */ + return 0; + } else { + /* TODO: pad buffer to flush completely */ + } + + /* calculate output size and reallocate output buffer if needed */ + /* TODO: try to calculate this without the dummy resample() run */ + if (!dst->read_only && dst->allow_realloc) { + out_samples = resample(c, NULL, NULL, NULL, c->buffer->nb_samples, + INT_MAX, 0); + ret = ff_audio_data_realloc(dst, out_samples); + if (ret < 0) { + av_log(c->avr, AV_LOG_ERROR, "error reallocating output\n"); + return ret; + } + } + + /* resample each channel plane */ + for (ch = 0; ch < c->buffer->channels; ch++) { + out_samples = resample(c, (void *)dst->data[ch], + (const void *)c->buffer->data[ch], &consumed, + c->buffer->nb_samples, dst->allocated_samples, + ch + 1 == c->buffer->channels); + } + if (out_samples < 0) { + av_log(c->avr, AV_LOG_ERROR, "error during resampling\n"); + return out_samples; + } + + /* drain consumed samples from the internal buffer */ + ff_audio_data_drain(c->buffer, consumed); + + av_dlog(c->avr, "resampled %d in + %d leftover to %d out + %d leftover\n", + in_samples, in_leftover, out_samples, c->buffer->nb_samples); + + dst->nb_samples = out_samples; + return 0; +} + +int avresample_get_delay(AVAudioResampleContext *avr) +{ + if (!avr->resample_needed || !avr->resample) + return 0; + + return avr->resample->buffer->nb_samples; +} diff --git a/lib/ffmpeg/libavresample/resample.h b/lib/ffmpeg/libavresample/resample.h new file mode 100644 index 0000000000..4544dab92f --- /dev/null +++ b/lib/ffmpeg/libavresample/resample.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_RESAMPLE_H +#define AVRESAMPLE_RESAMPLE_H + +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" + +/** + * Allocate and initialize a ResampleContext. + * + * The parameters in the AVAudioResampleContext are used to initialize the + * ResampleContext. + * + * @param avr AVAudioResampleContext + * @return newly-allocated ResampleContext + */ +ResampleContext *ff_audio_resample_init(AVAudioResampleContext *avr); + +/** + * Free a ResampleContext. + * + * @param c ResampleContext + */ +void ff_audio_resample_free(ResampleContext **c); + +/** + * Resample audio data. + * + * Changes the sample rate. + * + * @par + * All samples in the source data may not be consumed depending on the + * resampling parameters and the size of the output buffer. The unconsumed + * samples are automatically added to the start of the source in the next call. + * If the destination data can be reallocated, that may be done in this function + * in order to fit all available output. If it cannot be reallocated, fewer + * input samples will be consumed in order to have the output fit in the + * destination data buffers. + * + * @param c ResampleContext + * @param dst destination audio data + * @param src source audio data + * @return 0 on success, negative AVERROR code on failure + */ +int ff_audio_resample(ResampleContext *c, AudioData *dst, AudioData *src); + +#endif /* AVRESAMPLE_RESAMPLE_H */ diff --git a/lib/ffmpeg/libavresample/resample_template.c b/lib/ffmpeg/libavresample/resample_template.c new file mode 100644 index 0000000000..06da90fe9f --- /dev/null +++ b/lib/ffmpeg/libavresample/resample_template.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at> + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if defined(CONFIG_RESAMPLE_DBL) +#define SET_TYPE(func) func ## _dbl +#define FELEM double +#define FELEM2 double +#define FELEML double +#define OUT(d, v) d = v +#define DBL_TO_FELEM(d, v) d = v +#elif defined(CONFIG_RESAMPLE_FLT) +#define SET_TYPE(func) func ## _flt +#define FELEM float +#define FELEM2 float +#define FELEML float +#define OUT(d, v) d = v +#define DBL_TO_FELEM(d, v) d = v +#elif defined(CONFIG_RESAMPLE_S32) +#define SET_TYPE(func) func ## _s32 +#define FELEM int32_t +#define FELEM2 int64_t +#define FELEML int64_t +#define OUT(d, v) d = av_clipl_int32((v + (1 << 29)) >> 30) +#define DBL_TO_FELEM(d, v) d = av_clipl_int32(llrint(v * (1 << 30))); +#else +#define SET_TYPE(func) func ## _s16 +#define FELEM int16_t +#define FELEM2 int32_t +#define FELEML int64_t +#define OUT(d, v) d = av_clip_int16((v + (1 << 14)) >> 15) +#define DBL_TO_FELEM(d, v) d = av_clip_int16(lrint(v * (1 << 15))) +#endif + +static void SET_TYPE(resample_one)(ResampleContext *c, int no_filter, + void *dst0, int dst_index, const void *src0, + int src_size, int index, int frac) +{ + FELEM *dst = dst0; + const FELEM *src = src0; + + if (no_filter) { + dst[dst_index] = src[index]; + } else { + int i; + int sample_index = index >> c->phase_shift; + FELEM2 val = 0; + FELEM *filter = ((FELEM *)c->filter_bank) + + c->filter_length * (index & c->phase_mask); + + if (sample_index < 0) { + for (i = 0; i < c->filter_length; i++) + val += src[FFABS(sample_index + i) % src_size] * + (FELEM2)filter[i]; + } else if (c->linear) { + FELEM2 v2 = 0; + for (i = 0; i < c->filter_length; i++) { + val += src[abs(sample_index + i)] * (FELEM2)filter[i]; + v2 += src[abs(sample_index + i)] * (FELEM2)filter[i + c->filter_length]; + } + val += (v2 - val) * (FELEML)frac / c->src_incr; + } else { + for (i = 0; i < c->filter_length; i++) + val += src[sample_index + i] * (FELEM2)filter[i]; + } + + OUT(dst[dst_index], val); + } +} + +static void SET_TYPE(set_filter)(void *filter0, double *tab, int phase, + int tap_count) +{ + int i; + FELEM *filter = ((FELEM *)filter0) + phase * tap_count; + for (i = 0; i < tap_count; i++) { + DBL_TO_FELEM(filter[i], tab[i]); + } +} + +#undef SET_TYPE +#undef FELEM +#undef FELEM2 +#undef FELEML +#undef OUT +#undef DBL_TO_FELEM diff --git a/lib/ffmpeg/libavresample/utils.c b/lib/ffmpeg/libavresample/utils.c new file mode 100644 index 0000000000..c159e33206 --- /dev/null +++ b/lib/ffmpeg/libavresample/utils.c @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" +#include "libavutil/dict.h" +// #include "libavutil/error.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" + +#include "avresample.h" +#include "internal.h" +#include "audio_data.h" +#include "audio_convert.h" +#include "audio_mix.h" +#include "resample.h" + +int avresample_open(AVAudioResampleContext *avr) +{ + int ret; + + /* set channel mixing parameters */ + avr->in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + if (avr->in_channels <= 0 || avr->in_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid input channel layout: %"PRIu64"\n", + avr->in_channel_layout); + return AVERROR(EINVAL); + } + avr->out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + if (avr->out_channels <= 0 || avr->out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid output channel layout: %"PRIu64"\n", + avr->out_channel_layout); + return AVERROR(EINVAL); + } + avr->resample_channels = FFMIN(avr->in_channels, avr->out_channels); + avr->downmix_needed = avr->in_channels > avr->out_channels; + avr->upmix_needed = avr->out_channels > avr->in_channels || + (!avr->downmix_needed && (avr->mix_matrix || + avr->in_channel_layout != avr->out_channel_layout)); + avr->mixing_needed = avr->downmix_needed || avr->upmix_needed; + + /* set resampling parameters */ + avr->resample_needed = avr->in_sample_rate != avr->out_sample_rate || + avr->force_resampling; + + /* select internal sample format if not specified by the user */ + if (avr->internal_sample_fmt == AV_SAMPLE_FMT_NONE && + (avr->mixing_needed || avr->resample_needed)) { + enum AVSampleFormat in_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); + enum AVSampleFormat out_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); + int max_bps = FFMAX(av_get_bytes_per_sample(in_fmt), + av_get_bytes_per_sample(out_fmt)); + if (max_bps <= 2) { + avr->internal_sample_fmt = AV_SAMPLE_FMT_S16P; + } else if (avr->mixing_needed) { + avr->internal_sample_fmt = AV_SAMPLE_FMT_FLTP; + } else { + if (max_bps <= 4) { + if (in_fmt == AV_SAMPLE_FMT_S32P || + out_fmt == AV_SAMPLE_FMT_S32P) { + if (in_fmt == AV_SAMPLE_FMT_FLTP || + out_fmt == AV_SAMPLE_FMT_FLTP) { + /* if one is s32 and the other is flt, use dbl */ + avr->internal_sample_fmt = AV_SAMPLE_FMT_DBLP; + } else { + /* if one is s32 and the other is s32, s16, or u8, use s32 */ + avr->internal_sample_fmt = AV_SAMPLE_FMT_S32P; + } + } else { + /* if one is flt and the other is flt, s16 or u8, use flt */ + avr->internal_sample_fmt = AV_SAMPLE_FMT_FLTP; + } + } else { + /* if either is dbl, use dbl */ + avr->internal_sample_fmt = AV_SAMPLE_FMT_DBLP; + } + } + av_log(avr, AV_LOG_DEBUG, "Using %s as internal sample format\n", + av_get_sample_fmt_name(avr->internal_sample_fmt)); + } + + /* treat all mono as planar for easier comparison */ + if (avr->in_channels == 1) + avr->in_sample_fmt = av_get_planar_sample_fmt(avr->in_sample_fmt); + if (avr->out_channels == 1) + avr->out_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); + + /* we may need to add an extra conversion in order to remap channels if + the output format is not planar */ + if (avr->use_channel_map && !avr->mixing_needed && !avr->resample_needed && + !av_sample_fmt_is_planar(avr->out_sample_fmt)) { + avr->internal_sample_fmt = av_get_planar_sample_fmt(avr->out_sample_fmt); + } + + /* set sample format conversion parameters */ + if (avr->resample_needed || avr->mixing_needed) + avr->in_convert_needed = avr->in_sample_fmt != avr->internal_sample_fmt; + else + avr->in_convert_needed = avr->use_channel_map && + !av_sample_fmt_is_planar(avr->out_sample_fmt); + + if (avr->resample_needed || avr->mixing_needed || avr->in_convert_needed) + avr->out_convert_needed = avr->internal_sample_fmt != avr->out_sample_fmt; + else + avr->out_convert_needed = avr->in_sample_fmt != avr->out_sample_fmt; + + avr->in_copy_needed = !avr->in_convert_needed && (avr->mixing_needed || + (avr->use_channel_map && avr->resample_needed)); + + if (avr->use_channel_map) { + if (avr->in_copy_needed) { + avr->remap_point = REMAP_IN_COPY; + av_dlog(avr, "remap channels during in_copy\n"); + } else if (avr->in_convert_needed) { + avr->remap_point = REMAP_IN_CONVERT; + av_dlog(avr, "remap channels during in_convert\n"); + } else if (avr->out_convert_needed) { + avr->remap_point = REMAP_OUT_CONVERT; + av_dlog(avr, "remap channels during out_convert\n"); + } else { + avr->remap_point = REMAP_OUT_COPY; + av_dlog(avr, "remap channels during out_copy\n"); + } + +#ifdef DEBUG + { + int ch; + av_dlog(avr, "output map: "); + if (avr->ch_map_info.do_remap) + for (ch = 0; ch < avr->in_channels; ch++) + av_dlog(avr, " % 2d", avr->ch_map_info.channel_map[ch]); + else + av_dlog(avr, "n/a"); + av_dlog(avr, "\n"); + av_dlog(avr, "copy map: "); + if (avr->ch_map_info.do_copy) + for (ch = 0; ch < avr->in_channels; ch++) + av_dlog(avr, " % 2d", avr->ch_map_info.channel_copy[ch]); + else + av_dlog(avr, "n/a"); + av_dlog(avr, "\n"); + av_dlog(avr, "zero map: "); + if (avr->ch_map_info.do_zero) + for (ch = 0; ch < avr->in_channels; ch++) + av_dlog(avr, " % 2d", avr->ch_map_info.channel_zero[ch]); + else + av_dlog(avr, "n/a"); + av_dlog(avr, "\n"); + av_dlog(avr, "input map: "); + for (ch = 0; ch < avr->in_channels; ch++) + av_dlog(avr, " % 2d", avr->ch_map_info.input_map[ch]); + av_dlog(avr, "\n"); + } +#endif + } else + avr->remap_point = REMAP_NONE; + + /* allocate buffers */ + if (avr->in_copy_needed || avr->in_convert_needed) { + avr->in_buffer = ff_audio_data_alloc(FFMAX(avr->in_channels, avr->out_channels), + 0, avr->internal_sample_fmt, + "in_buffer"); + if (!avr->in_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + if (avr->resample_needed) { + avr->resample_out_buffer = ff_audio_data_alloc(avr->out_channels, + 0, avr->internal_sample_fmt, + "resample_out_buffer"); + if (!avr->resample_out_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + if (avr->out_convert_needed) { + avr->out_buffer = ff_audio_data_alloc(avr->out_channels, 0, + avr->out_sample_fmt, "out_buffer"); + if (!avr->out_buffer) { + ret = AVERROR(EINVAL); + goto error; + } + } + avr->out_fifo = av_audio_fifo_alloc(avr->out_sample_fmt, avr->out_channels, + 1024); + if (!avr->out_fifo) { + ret = AVERROR(ENOMEM); + goto error; + } + + /* setup contexts */ + if (avr->in_convert_needed) { + avr->ac_in = ff_audio_convert_alloc(avr, avr->internal_sample_fmt, + avr->in_sample_fmt, avr->in_channels, + avr->in_sample_rate, + avr->remap_point == REMAP_IN_CONVERT); + if (!avr->ac_in) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->out_convert_needed) { + enum AVSampleFormat src_fmt; + if (avr->in_convert_needed) + src_fmt = avr->internal_sample_fmt; + else + src_fmt = avr->in_sample_fmt; + avr->ac_out = ff_audio_convert_alloc(avr, avr->out_sample_fmt, src_fmt, + avr->out_channels, + avr->out_sample_rate, + avr->remap_point == REMAP_OUT_CONVERT); + if (!avr->ac_out) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->resample_needed) { + avr->resample = ff_audio_resample_init(avr); + if (!avr->resample) { + ret = AVERROR(ENOMEM); + goto error; + } + } + if (avr->mixing_needed) { + avr->am = ff_audio_mix_alloc(avr); + if (!avr->am) { + ret = AVERROR(ENOMEM); + goto error; + } + } + + return 0; + +error: + avresample_close(avr); + return ret; +} + +void avresample_close(AVAudioResampleContext *avr) +{ + ff_audio_data_free(&avr->in_buffer); + ff_audio_data_free(&avr->resample_out_buffer); + ff_audio_data_free(&avr->out_buffer); + av_audio_fifo_free(avr->out_fifo); + avr->out_fifo = NULL; + ff_audio_convert_free(&avr->ac_in); + ff_audio_convert_free(&avr->ac_out); + ff_audio_resample_free(&avr->resample); + ff_audio_mix_free(&avr->am); + av_freep(&avr->mix_matrix); + + avr->use_channel_map = 0; +} + +void avresample_free(AVAudioResampleContext **avr) +{ + if (!*avr) + return; + avresample_close(*avr); + av_opt_free(*avr); + av_freep(avr); +} + +static int handle_buffered_output(AVAudioResampleContext *avr, + AudioData *output, AudioData *converted) +{ + int ret; + + if (!output || av_audio_fifo_size(avr->out_fifo) > 0 || + (converted && output->allocated_samples < converted->nb_samples)) { + if (converted) { + /* if there are any samples in the output FIFO or if the + user-supplied output buffer is not large enough for all samples, + we add to the output FIFO */ + av_dlog(avr, "[FIFO] add %s to out_fifo\n", converted->name); + ret = ff_audio_data_add_to_fifo(avr->out_fifo, converted, 0, + converted->nb_samples); + if (ret < 0) + return ret; + } + + /* if the user specified an output buffer, read samples from the output + FIFO to the user output */ + if (output && output->allocated_samples > 0) { + av_dlog(avr, "[FIFO] read from out_fifo to output\n"); + av_dlog(avr, "[end conversion]\n"); + return ff_audio_data_read_from_fifo(avr->out_fifo, output, + output->allocated_samples); + } + } else if (converted) { + /* copy directly to output if it is large enough or there is not any + data in the output FIFO */ + av_dlog(avr, "[copy] %s to output\n", converted->name); + output->nb_samples = 0; + ret = ff_audio_data_copy(output, converted, + avr->remap_point == REMAP_OUT_COPY ? + &avr->ch_map_info : NULL); + if (ret < 0) + return ret; + av_dlog(avr, "[end conversion]\n"); + return output->nb_samples; + } + av_dlog(avr, "[end conversion]\n"); + return 0; +} + +int attribute_align_arg avresample_convert(AVAudioResampleContext *avr, + uint8_t **output, int out_plane_size, + int out_samples, uint8_t **input, + int in_plane_size, int in_samples) +{ + AudioData input_buffer; + AudioData output_buffer; + AudioData *current_buffer; + int ret, direct_output; + + /* reset internal buffers */ + if (avr->in_buffer) { + avr->in_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->in_buffer, + avr->in_buffer->allocated_channels); + } + if (avr->resample_out_buffer) { + avr->resample_out_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->resample_out_buffer, + avr->resample_out_buffer->allocated_channels); + } + if (avr->out_buffer) { + avr->out_buffer->nb_samples = 0; + ff_audio_data_set_channels(avr->out_buffer, + avr->out_buffer->allocated_channels); + } + + av_dlog(avr, "[start conversion]\n"); + + /* initialize output_buffer with output data */ + direct_output = output && av_audio_fifo_size(avr->out_fifo) == 0; + if (output) { + ret = ff_audio_data_init(&output_buffer, output, out_plane_size, + avr->out_channels, out_samples, + avr->out_sample_fmt, 0, "output"); + if (ret < 0) + return ret; + output_buffer.nb_samples = 0; + } + + if (input) { + /* initialize input_buffer with input data */ + ret = ff_audio_data_init(&input_buffer, input, in_plane_size, + avr->in_channels, in_samples, + avr->in_sample_fmt, 1, "input"); + if (ret < 0) + return ret; + current_buffer = &input_buffer; + + if (avr->upmix_needed && !avr->in_convert_needed && !avr->resample_needed && + !avr->out_convert_needed && direct_output && out_samples >= in_samples) { + /* in some rare cases we can copy input to output and upmix + directly in the output buffer */ + av_dlog(avr, "[copy] %s to output\n", current_buffer->name); + ret = ff_audio_data_copy(&output_buffer, current_buffer, + avr->remap_point == REMAP_OUT_COPY ? + &avr->ch_map_info : NULL); + if (ret < 0) + return ret; + current_buffer = &output_buffer; + } else if (avr->remap_point == REMAP_OUT_COPY && + (!direct_output || out_samples < in_samples)) { + /* if remapping channels during output copy, we may need to + * use an intermediate buffer in order to remap before adding + * samples to the output fifo */ + av_dlog(avr, "[copy] %s to out_buffer\n", current_buffer->name); + ret = ff_audio_data_copy(avr->out_buffer, current_buffer, + &avr->ch_map_info); + if (ret < 0) + return ret; + current_buffer = avr->out_buffer; + } else if (avr->in_copy_needed || avr->in_convert_needed) { + /* if needed, copy or convert input to in_buffer, and downmix if + applicable */ + if (avr->in_convert_needed) { + ret = ff_audio_data_realloc(avr->in_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + av_dlog(avr, "[convert] %s to in_buffer\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_in, avr->in_buffer, + current_buffer); + if (ret < 0) + return ret; + } else { + av_dlog(avr, "[copy] %s to in_buffer\n", current_buffer->name); + ret = ff_audio_data_copy(avr->in_buffer, current_buffer, + avr->remap_point == REMAP_IN_COPY ? + &avr->ch_map_info : NULL); + if (ret < 0) + return ret; + } + ff_audio_data_set_channels(avr->in_buffer, avr->in_channels); + if (avr->downmix_needed) { + av_dlog(avr, "[downmix] in_buffer\n"); + ret = ff_audio_mix(avr->am, avr->in_buffer); + if (ret < 0) + return ret; + } + current_buffer = avr->in_buffer; + } + } else { + /* flush resampling buffer and/or output FIFO if input is NULL */ + if (!avr->resample_needed) + return handle_buffered_output(avr, output ? &output_buffer : NULL, + NULL); + current_buffer = NULL; + } + + if (avr->resample_needed) { + AudioData *resample_out; + + if (!avr->out_convert_needed && direct_output && out_samples > 0) + resample_out = &output_buffer; + else + resample_out = avr->resample_out_buffer; + av_dlog(avr, "[resample] %s to %s\n", current_buffer->name, + resample_out->name); + ret = ff_audio_resample(avr->resample, resample_out, + current_buffer); + if (ret < 0) + return ret; + + /* if resampling did not produce any samples, just return 0 */ + if (resample_out->nb_samples == 0) { + av_dlog(avr, "[end conversion]\n"); + return 0; + } + + current_buffer = resample_out; + } + + if (avr->upmix_needed) { + av_dlog(avr, "[upmix] %s\n", current_buffer->name); + ret = ff_audio_mix(avr->am, current_buffer); + if (ret < 0) + return ret; + } + + /* if we resampled or upmixed directly to output, return here */ + if (current_buffer == &output_buffer) { + av_dlog(avr, "[end conversion]\n"); + return current_buffer->nb_samples; + } + + if (avr->out_convert_needed) { + if (direct_output && out_samples >= current_buffer->nb_samples) { + /* convert directly to output */ + av_dlog(avr, "[convert] %s to output\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_out, &output_buffer, current_buffer); + if (ret < 0) + return ret; + + av_dlog(avr, "[end conversion]\n"); + return output_buffer.nb_samples; + } else { + ret = ff_audio_data_realloc(avr->out_buffer, + current_buffer->nb_samples); + if (ret < 0) + return ret; + av_dlog(avr, "[convert] %s to out_buffer\n", current_buffer->name); + ret = ff_audio_convert(avr->ac_out, avr->out_buffer, + current_buffer); + if (ret < 0) + return ret; + current_buffer = avr->out_buffer; + } + } + + return handle_buffered_output(avr, output ? &output_buffer : NULL, + current_buffer); +} + +int avresample_get_matrix(AVAudioResampleContext *avr, double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + if (avr->am) + return ff_audio_mix_get_matrix(avr->am, matrix, stride); + + in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + + if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + if (!avr->mix_matrix) { + av_log(avr, AV_LOG_ERROR, "matrix is not set\n"); + return AVERROR(EINVAL); + } + + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + matrix[o * stride + i] = avr->mix_matrix[o * in_channels + i]; + + return 0; +} + +int avresample_set_matrix(AVAudioResampleContext *avr, const double *matrix, + int stride) +{ + int in_channels, out_channels, i, o; + + if (avr->am) + return ff_audio_mix_set_matrix(avr->am, matrix, stride); + + in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + out_channels = av_get_channel_layout_nb_channels(avr->out_channel_layout); + + if ( in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS || + out_channels <= 0 || out_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid channel layouts\n"); + return AVERROR(EINVAL); + } + + if (avr->mix_matrix) + av_freep(&avr->mix_matrix); + avr->mix_matrix = av_malloc(in_channels * out_channels * + sizeof(*avr->mix_matrix)); + if (!avr->mix_matrix) + return AVERROR(ENOMEM); + + for (o = 0; o < out_channels; o++) + for (i = 0; i < in_channels; i++) + avr->mix_matrix[o * in_channels + i] = matrix[o * stride + i]; + + return 0; +} + +int avresample_set_channel_mapping(AVAudioResampleContext *avr, + const int *channel_map) +{ + ChannelMapInfo *info = &avr->ch_map_info; + int in_channels, ch, i; + + in_channels = av_get_channel_layout_nb_channels(avr->in_channel_layout); + if (in_channels <= 0 || in_channels > AVRESAMPLE_MAX_CHANNELS) { + av_log(avr, AV_LOG_ERROR, "Invalid input channel layout\n"); + return AVERROR(EINVAL); + } + + memset(info, 0, sizeof(*info)); + memset(info->input_map, -1, sizeof(info->input_map)); + + for (ch = 0; ch < in_channels; ch++) { + if (channel_map[ch] >= in_channels) { + av_log(avr, AV_LOG_ERROR, "Invalid channel map\n"); + return AVERROR(EINVAL); + } + if (channel_map[ch] < 0) { + info->channel_zero[ch] = 1; + info->channel_map[ch] = -1; + info->do_zero = 1; + } else if (info->input_map[channel_map[ch]] >= 0) { + info->channel_copy[ch] = info->input_map[channel_map[ch]]; + info->channel_map[ch] = -1; + info->do_copy = 1; + } else { + info->channel_map[ch] = channel_map[ch]; + info->input_map[channel_map[ch]] = ch; + info->do_remap = 1; + } + } + /* Fill-in unmapped input channels with unmapped output channels. + This is used when remapping during conversion from interleaved to + planar format. */ + for (ch = 0, i = 0; ch < in_channels && i < in_channels; ch++, i++) { + while (ch < in_channels && info->input_map[ch] >= 0) + ch++; + while (i < in_channels && info->channel_map[i] >= 0) + i++; + if (ch >= in_channels || i >= in_channels) + break; + info->input_map[ch] = i; + } + + avr->use_channel_map = 1; + return 0; +} + +int avresample_available(AVAudioResampleContext *avr) +{ + return av_audio_fifo_size(avr->out_fifo); +} + +int avresample_read(AVAudioResampleContext *avr, uint8_t **output, int nb_samples) +{ + if (!output) + return av_audio_fifo_drain(avr->out_fifo, nb_samples); + return av_audio_fifo_read(avr->out_fifo, (void**)output, nb_samples); +} + +unsigned avresample_version(void) +{ + return LIBAVRESAMPLE_VERSION_INT; +} + +const char *avresample_license(void) +{ +#define LICENSE_PREFIX "libavresample license: " + return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1; +} + +const char *avresample_configuration(void) +{ + return FFMPEG_CONFIGURATION; +} diff --git a/lib/ffmpeg/libavresample/version.h b/lib/ffmpeg/libavresample/version.h new file mode 100644 index 0000000000..387d097d3a --- /dev/null +++ b/lib/ffmpeg/libavresample/version.h @@ -0,0 +1,46 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVRESAMPLE_VERSION_H +#define AVRESAMPLE_VERSION_H + +#define LIBAVRESAMPLE_VERSION_MAJOR 1 +#define LIBAVRESAMPLE_VERSION_MINOR 1 +#define LIBAVRESAMPLE_VERSION_MICRO 0 + +#define LIBAVRESAMPLE_VERSION_INT AV_VERSION_INT(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_VERSION AV_VERSION(LIBAVRESAMPLE_VERSION_MAJOR, \ + LIBAVRESAMPLE_VERSION_MINOR, \ + LIBAVRESAMPLE_VERSION_MICRO) +#define LIBAVRESAMPLE_BUILD LIBAVRESAMPLE_VERSION_INT + +#define LIBAVRESAMPLE_IDENT "Lavr" AV_STRINGIFY(LIBAVRESAMPLE_VERSION) + +/** + * FF_API_* defines may be placed below to indicate public API that will be + * dropped at a future version bump. The defines themselves are not part of + * the public API and may change, break or disappear at any time. + */ + +#ifndef FF_API_RESAMPLE_CLOSE_OPEN +#define FF_API_RESAMPLE_CLOSE_OPEN (LIBAVRESAMPLE_VERSION_MAJOR < 2) +#endif + +#endif /* AVRESAMPLE_VERSION_H */ diff --git a/lib/ffmpeg/libavresample/x86/Makefile b/lib/ffmpeg/libavresample/x86/Makefile new file mode 100644 index 0000000000..2e8786fb90 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/Makefile @@ -0,0 +1,7 @@ +OBJS += x86/audio_convert_init.o \ + x86/audio_mix_init.o \ + x86/dither_init.o \ + +YASM-OBJS += x86/audio_convert.o \ + x86/audio_mix.o \ + x86/dither.o \ diff --git a/lib/ffmpeg/libavresample/x86/audio_convert.asm b/lib/ffmpeg/libavresample/x86/audio_convert.asm new file mode 100644 index 0000000000..1d125c2b50 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/audio_convert.asm @@ -0,0 +1,1261 @@ +;****************************************************************************** +;* x86 optimized Format Conversion Utils +;* Copyright (c) 2008 Loren Merritt +;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> +;* +;* This file is part of Libav. +;* +;* Libav is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* Libav is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General Public +;* License along with Libav; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" +%include "util.asm" + +SECTION_RODATA 32 + +pf_s32_inv_scale: times 8 dd 0x30000000 +pf_s32_scale: times 8 dd 0x4f000000 +pf_s32_clip: times 8 dd 0x4effffff +pf_s16_inv_scale: times 4 dd 0x38000000 +pf_s16_scale: times 4 dd 0x47000000 +pb_shuf_unpack_even: db -1, -1, 0, 1, -1, -1, 2, 3, -1, -1, 8, 9, -1, -1, 10, 11 +pb_shuf_unpack_odd: db -1, -1, 4, 5, -1, -1, 6, 7, -1, -1, 12, 13, -1, -1, 14, 15 +pb_interleave_words: SHUFFLE_MASK_W 0, 4, 1, 5, 2, 6, 3, 7 +pb_deinterleave_words: SHUFFLE_MASK_W 0, 2, 4, 6, 1, 3, 5, 7 +pw_zero_even: times 4 dw 0x0000, 0xffff + +SECTION_TEXT + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_s32(int32_t *dst, const int16_t *src, int len); +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal conv_s16_to_s32, 3,3,3, dst, src, len + lea lenq, [2*lend] + lea dstq, [dstq+2*lenq] + add srcq, lenq + neg lenq +.loop: + mova m2, [srcq+lenq] + pxor m0, m0 + pxor m1, m1 + punpcklwd m0, m2 + punpckhwd m1, m2 + mova [dstq+2*lenq ], m0 + mova [dstq+2*lenq+mmsize], m1 + add lenq, mmsize + jl .loop + REP_RET + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_flt(float *dst, const int16_t *src, int len); +;------------------------------------------------------------------------------ + +%macro CONV_S16_TO_FLT 0 +cglobal conv_s16_to_flt, 3,3,3, dst, src, len + lea lenq, [2*lend] + add srcq, lenq + lea dstq, [dstq + 2*lenq] + neg lenq + mova m2, [pf_s16_inv_scale] + ALIGN 16 +.loop: + mova m0, [srcq+lenq] + S16_TO_S32_SX 0, 1 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + mulps m0, m2 + mulps m1, m2 + mova [dstq+2*lenq ], m0 + mova [dstq+2*lenq+mmsize], m1 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16_TO_FLT +INIT_XMM sse4 +CONV_S16_TO_FLT + +;------------------------------------------------------------------------------ +; void ff_conv_s32_to_s16(int16_t *dst, const int32_t *src, int len); +;------------------------------------------------------------------------------ + +%macro CONV_S32_TO_S16 0 +cglobal conv_s32_to_s16, 3,3,4, dst, src, len + lea lenq, [2*lend] + lea srcq, [srcq+2*lenq] + add dstq, lenq + neg lenq +.loop: + mova m0, [srcq+2*lenq ] + mova m1, [srcq+2*lenq+ mmsize] + mova m2, [srcq+2*lenq+2*mmsize] + mova m3, [srcq+2*lenq+3*mmsize] + psrad m0, 16 + psrad m1, 16 + psrad m2, 16 + psrad m3, 16 + packssdw m0, m1 + packssdw m2, m3 + mova [dstq+lenq ], m0 + mova [dstq+lenq+mmsize], m2 + add lenq, mmsize*2 + jl .loop +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + +INIT_MMX mmx +CONV_S32_TO_S16 +INIT_XMM sse2 +CONV_S32_TO_S16 + +;------------------------------------------------------------------------------ +; void ff_conv_s32_to_flt(float *dst, const int32_t *src, int len); +;------------------------------------------------------------------------------ + +%macro CONV_S32_TO_FLT 0 +cglobal conv_s32_to_flt, 3,3,3, dst, src, len + lea lenq, [4*lend] + add srcq, lenq + add dstq, lenq + neg lenq + mova m0, [pf_s32_inv_scale] + ALIGN 16 +.loop: + cvtdq2ps m1, [srcq+lenq ] + cvtdq2ps m2, [srcq+lenq+mmsize] + mulps m1, m1, m0 + mulps m2, m2, m0 + mova [dstq+lenq ], m1 + mova [dstq+lenq+mmsize], m2 + add lenq, mmsize*2 + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S32_TO_FLT +%if HAVE_AVX_EXTERNAL +INIT_YMM avx +CONV_S32_TO_FLT +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_s16(int16_t *dst, const float *src, int len); +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal conv_flt_to_s16, 3,3,5, dst, src, len + lea lenq, [2*lend] + lea srcq, [srcq+2*lenq] + add dstq, lenq + neg lenq + mova m4, [pf_s16_scale] +.loop: + mova m0, [srcq+2*lenq ] + mova m1, [srcq+2*lenq+1*mmsize] + mova m2, [srcq+2*lenq+2*mmsize] + mova m3, [srcq+2*lenq+3*mmsize] + mulps m0, m4 + mulps m1, m4 + mulps m2, m4 + mulps m3, m4 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + packssdw m0, m1 + packssdw m2, m3 + mova [dstq+lenq ], m0 + mova [dstq+lenq+mmsize], m2 + add lenq, mmsize*2 + jl .loop + REP_RET + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_s32(int32_t *dst, const float *src, int len); +;------------------------------------------------------------------------------ + +%macro CONV_FLT_TO_S32 0 +cglobal conv_flt_to_s32, 3,3,6, dst, src, len + lea lenq, [lend*4] + add srcq, lenq + add dstq, lenq + neg lenq + mova m4, [pf_s32_scale] + mova m5, [pf_s32_clip] +.loop: + mulps m0, m4, [srcq+lenq ] + mulps m1, m4, [srcq+lenq+1*mmsize] + mulps m2, m4, [srcq+lenq+2*mmsize] + mulps m3, m4, [srcq+lenq+3*mmsize] + minps m0, m0, m5 + minps m1, m1, m5 + minps m2, m2, m5 + minps m3, m3, m5 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + mova [dstq+lenq ], m0 + mova [dstq+lenq+1*mmsize], m1 + mova [dstq+lenq+2*mmsize], m2 + mova [dstq+lenq+3*mmsize], m3 + add lenq, mmsize*4 + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_FLT_TO_S32 +%if HAVE_AVX_EXTERNAL +INIT_YMM avx +CONV_FLT_TO_S32 +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16p_to_s16_2ch(int16_t *dst, int16_t *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16P_TO_S16_2CH 0 +cglobal conv_s16p_to_s16_2ch, 3,4,5, dst, src0, len, src1 + mov src1q, [src0q+gprsize] + mov src0q, [src0q ] + lea lenq, [2*lend] + add src0q, lenq + add src1q, lenq + lea dstq, [dstq+2*lenq] + neg lenq +.loop: + mova m0, [src0q+lenq ] + mova m1, [src1q+lenq ] + mova m2, [src0q+lenq+mmsize] + mova m3, [src1q+lenq+mmsize] + SBUTTERFLY2 wd, 0, 1, 4 + SBUTTERFLY2 wd, 2, 3, 4 + mova [dstq+2*lenq+0*mmsize], m0 + mova [dstq+2*lenq+1*mmsize], m1 + mova [dstq+2*lenq+2*mmsize], m2 + mova [dstq+2*lenq+3*mmsize], m3 + add lenq, 2*mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16P_TO_S16_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16P_TO_S16_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16p_to_s16_6ch(int16_t *dst, int16_t *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +;------------------------------------------------------------------------------ +; NOTE: In the 6-channel functions, len could be used as an index on x86-64 +; instead of just a counter, which would avoid incrementing the +; pointers, but the extra complexity and amount of code is not worth +; the small gain. On x86-32 there are not enough registers to use len +; as an index without keeping two of the pointers on the stack and +; loading them in each iteration. +;------------------------------------------------------------------------------ + +%macro CONV_S16P_TO_S16_6CH 0 +%if ARCH_X86_64 +cglobal conv_s16p_to_s16_6ch, 3,8,7, dst, src0, len, src1, src2, src3, src4, src5 +%else +cglobal conv_s16p_to_s16_6ch, 2,7,7, dst, src0, src1, src2, src3, src4, src5 +%define lend dword r2m +%endif + mov src1q, [src0q+1*gprsize] + mov src2q, [src0q+2*gprsize] + mov src3q, [src0q+3*gprsize] + mov src4q, [src0q+4*gprsize] + mov src5q, [src0q+5*gprsize] + mov src0q, [src0q] + sub src1q, src0q + sub src2q, src0q + sub src3q, src0q + sub src4q, src0q + sub src5q, src0q +.loop: +%if cpuflag(sse2slow) + movq m0, [src0q ] ; m0 = 0, 6, 12, 18, x, x, x, x + movq m1, [src0q+src1q] ; m1 = 1, 7, 13, 19, x, x, x, x + movq m2, [src0q+src2q] ; m2 = 2, 8, 14, 20, x, x, x, x + movq m3, [src0q+src3q] ; m3 = 3, 9, 15, 21, x, x, x, x + movq m4, [src0q+src4q] ; m4 = 4, 10, 16, 22, x, x, x, x + movq m5, [src0q+src5q] ; m5 = 5, 11, 17, 23, x, x, x, x + ; unpack words: + punpcklwd m0, m1 ; m0 = 0, 1, 6, 7, 12, 13, 18, 19 + punpcklwd m2, m3 ; m2 = 4, 5, 10, 11, 16, 17, 22, 23 + punpcklwd m4, m5 ; m4 = 2, 3, 8, 9, 14, 15, 20, 21 + ; blend dwords + shufps m1, m0, m2, q2020 ; m1 = 0, 1, 12, 13, 2, 3, 14, 15 + shufps m0, m4, q2031 ; m0 = 6, 7, 18, 19, 4, 5, 16, 17 + shufps m2, m4, q3131 ; m2 = 8, 9, 20, 21, 10, 11, 22, 23 + ; shuffle dwords + pshufd m0, m0, q1302 ; m0 = 4, 5, 6, 7, 16, 17, 18, 19 + pshufd m1, m1, q3120 ; m1 = 0, 1, 2, 3, 12, 13, 14, 15 + pshufd m2, m2, q3120 ; m2 = 8, 9, 10, 11, 20, 21, 22, 23 + movq [dstq+0*mmsize/2], m1 + movq [dstq+1*mmsize/2], m0 + movq [dstq+2*mmsize/2], m2 + movhps [dstq+3*mmsize/2], m1 + movhps [dstq+4*mmsize/2], m0 + movhps [dstq+5*mmsize/2], m2 + add src0q, mmsize/2 + add dstq, mmsize*3 + sub lend, mmsize/4 +%else + mova m0, [src0q ] ; m0 = 0, 6, 12, 18, 24, 30, 36, 42 + mova m1, [src0q+src1q] ; m1 = 1, 7, 13, 19, 25, 31, 37, 43 + mova m2, [src0q+src2q] ; m2 = 2, 8, 14, 20, 26, 32, 38, 44 + mova m3, [src0q+src3q] ; m3 = 3, 9, 15, 21, 27, 33, 39, 45 + mova m4, [src0q+src4q] ; m4 = 4, 10, 16, 22, 28, 34, 40, 46 + mova m5, [src0q+src5q] ; m5 = 5, 11, 17, 23, 29, 35, 41, 47 + ; unpack words: + SBUTTERFLY2 wd, 0, 1, 6 ; m0 = 0, 1, 6, 7, 12, 13, 18, 19 + ; m1 = 24, 25, 30, 31, 36, 37, 42, 43 + SBUTTERFLY2 wd, 2, 3, 6 ; m2 = 2, 3, 8, 9, 14, 15, 20, 21 + ; m3 = 26, 27, 32, 33, 38, 39, 44, 45 + SBUTTERFLY2 wd, 4, 5, 6 ; m4 = 4, 5, 10, 11, 16, 17, 22, 23 + ; m5 = 28, 29, 34, 35, 40, 41, 46, 47 + ; blend dwords + shufps m6, m0, m2, q2020 ; m6 = 0, 1, 12, 13, 2, 3, 14, 15 + shufps m0, m4, q2031 ; m0 = 6, 7, 18, 19, 4, 5, 16, 17 + shufps m2, m4, q3131 ; m2 = 8, 9, 20, 21, 10, 11, 22, 23 + SWAP 4,6 ; m4 = 0, 1, 12, 13, 2, 3, 14, 15 + shufps m6, m1, m3, q2020 ; m6 = 24, 25, 36, 37, 26, 27, 38, 39 + shufps m1, m5, q2031 ; m1 = 30, 31, 42, 43, 28, 29, 40, 41 + shufps m3, m5, q3131 ; m3 = 32, 33, 44, 45, 34, 35, 46, 47 + SWAP 5,6 ; m5 = 24, 25, 36, 37, 26, 27, 38, 39 + ; shuffle dwords + pshufd m0, m0, q1302 ; m0 = 4, 5, 6, 7, 16, 17, 18, 19 + pshufd m2, m2, q3120 ; m2 = 8, 9, 10, 11, 20, 21, 22, 23 + pshufd m4, m4, q3120 ; m4 = 0, 1, 2, 3, 12, 13, 14, 15 + pshufd m1, m1, q1302 ; m1 = 28, 29, 30, 31, 40, 41, 42, 43 + pshufd m3, m3, q3120 ; m3 = 32, 33, 34, 35, 44, 45, 46, 47 + pshufd m5, m5, q3120 ; m5 = 24, 25, 26, 27, 36, 37, 38, 39 + ; shuffle qwords + punpcklqdq m6, m4, m0 ; m6 = 0, 1, 2, 3, 4, 5, 6, 7 + punpckhqdq m0, m2 ; m0 = 16, 17, 18, 19, 20, 21, 22, 23 + shufps m2, m4, q3210 ; m2 = 8, 9, 10, 11, 12, 13, 14, 15 + SWAP 4,6 ; m4 = 0, 1, 2, 3, 4, 5, 6, 7 + punpcklqdq m6, m5, m1 ; m6 = 24, 25, 26, 27, 28, 29, 30, 31 + punpckhqdq m1, m3 ; m1 = 40, 41, 42, 43, 44, 45, 46, 47 + shufps m3, m5, q3210 ; m3 = 32, 33, 34, 35, 36, 37, 38, 39 + SWAP 5,6 ; m5 = 24, 25, 26, 27, 28, 29, 30, 31 + mova [dstq+0*mmsize], m4 + mova [dstq+1*mmsize], m2 + mova [dstq+2*mmsize], m0 + mova [dstq+3*mmsize], m5 + mova [dstq+4*mmsize], m3 + mova [dstq+5*mmsize], m1 + add src0q, mmsize + add dstq, mmsize*6 + sub lend, mmsize/2 +%endif + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16P_TO_S16_6CH +INIT_XMM sse2slow +CONV_S16P_TO_S16_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16P_TO_S16_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16p_to_flt_2ch(float *dst, int16_t *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16P_TO_FLT_2CH 0 +cglobal conv_s16p_to_flt_2ch, 3,4,6, dst, src0, len, src1 + lea lenq, [2*lend] + mov src1q, [src0q+gprsize] + mov src0q, [src0q ] + lea dstq, [dstq+4*lenq] + add src0q, lenq + add src1q, lenq + neg lenq + mova m5, [pf_s32_inv_scale] +.loop: + mova m2, [src0q+lenq] ; m2 = 0, 2, 4, 6, 8, 10, 12, 14 + mova m4, [src1q+lenq] ; m4 = 1, 3, 5, 7, 9, 11, 13, 15 + SBUTTERFLY2 wd, 2, 4, 3 ; m2 = 0, 1, 2, 3, 4, 5, 6, 7 + ; m4 = 8, 9, 10, 11, 12, 13, 14, 15 + pxor m3, m3 + punpcklwd m0, m3, m2 ; m0 = 0, 1, 2, 3 + punpckhwd m1, m3, m2 ; m1 = 4, 5, 6, 7 + punpcklwd m2, m3, m4 ; m2 = 8, 9, 10, 11 + punpckhwd m3, m4 ; m3 = 12, 13, 14, 15 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + mulps m0, m5 + mulps m1, m5 + mulps m2, m5 + mulps m3, m5 + mova [dstq+4*lenq ], m0 + mova [dstq+4*lenq+ mmsize], m1 + mova [dstq+4*lenq+2*mmsize], m2 + mova [dstq+4*lenq+3*mmsize], m3 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16P_TO_FLT_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16P_TO_FLT_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16p_to_flt_6ch(float *dst, int16_t *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16P_TO_FLT_6CH 0 +%if ARCH_X86_64 +cglobal conv_s16p_to_flt_6ch, 3,8,8, dst, src, len, src1, src2, src3, src4, src5 +%else +cglobal conv_s16p_to_flt_6ch, 2,7,8, dst, src, src1, src2, src3, src4, src5 +%define lend dword r2m +%endif + mov src1q, [srcq+1*gprsize] + mov src2q, [srcq+2*gprsize] + mov src3q, [srcq+3*gprsize] + mov src4q, [srcq+4*gprsize] + mov src5q, [srcq+5*gprsize] + mov srcq, [srcq] + sub src1q, srcq + sub src2q, srcq + sub src3q, srcq + sub src4q, srcq + sub src5q, srcq + mova m7, [pf_s32_inv_scale] +%if cpuflag(ssse3) + %define unpack_even m6 + mova m6, [pb_shuf_unpack_even] +%if ARCH_X86_64 + %define unpack_odd m8 + mova m8, [pb_shuf_unpack_odd] +%else + %define unpack_odd [pb_shuf_unpack_odd] +%endif +%endif +.loop: + movq m0, [srcq ] ; m0 = 0, 6, 12, 18, x, x, x, x + movq m1, [srcq+src1q] ; m1 = 1, 7, 13, 19, x, x, x, x + movq m2, [srcq+src2q] ; m2 = 2, 8, 14, 20, x, x, x, x + movq m3, [srcq+src3q] ; m3 = 3, 9, 15, 21, x, x, x, x + movq m4, [srcq+src4q] ; m4 = 4, 10, 16, 22, x, x, x, x + movq m5, [srcq+src5q] ; m5 = 5, 11, 17, 23, x, x, x, x + ; unpack words: + punpcklwd m0, m1 ; m0 = 0, 1, 6, 7, 12, 13, 18, 19 + punpcklwd m2, m3 ; m2 = 2, 3, 8, 9, 14, 15, 20, 21 + punpcklwd m4, m5 ; m4 = 4, 5, 10, 11, 16, 17, 22, 23 + ; blend dwords + shufps m1, m4, m0, q3120 ; m1 = 4, 5, 16, 17, 6, 7, 18, 19 + shufps m0, m2, q2020 ; m0 = 0, 1, 12, 13, 2, 3, 14, 15 + shufps m2, m4, q3131 ; m2 = 8, 9, 20, 21, 10, 11, 22, 23 +%if cpuflag(ssse3) + pshufb m3, m0, unpack_odd ; m3 = 12, 13, 14, 15 + pshufb m0, unpack_even ; m0 = 0, 1, 2, 3 + pshufb m4, m1, unpack_odd ; m4 = 16, 17, 18, 19 + pshufb m1, unpack_even ; m1 = 4, 5, 6, 7 + pshufb m5, m2, unpack_odd ; m5 = 20, 21, 22, 23 + pshufb m2, unpack_even ; m2 = 8, 9, 10, 11 +%else + ; shuffle dwords + pshufd m0, m0, q3120 ; m0 = 0, 1, 2, 3, 12, 13, 14, 15 + pshufd m1, m1, q3120 ; m1 = 4, 5, 6, 7, 16, 17, 18, 19 + pshufd m2, m2, q3120 ; m2 = 8, 9, 10, 11, 20, 21, 22, 23 + pxor m6, m6 ; convert s16 in m0-m2 to s32 in m0-m5 + punpcklwd m3, m6, m0 ; m3 = 0, 1, 2, 3 + punpckhwd m4, m6, m0 ; m4 = 12, 13, 14, 15 + punpcklwd m0, m6, m1 ; m0 = 4, 5, 6, 7 + punpckhwd m5, m6, m1 ; m5 = 16, 17, 18, 19 + punpcklwd m1, m6, m2 ; m1 = 8, 9, 10, 11 + punpckhwd m6, m2 ; m6 = 20, 21, 22, 23 + SWAP 6,2,1,0,3,4,5 ; swap registers 3,0,1,4,5,6 to 0,1,2,3,4,5 +%endif + cvtdq2ps m0, m0 ; convert s32 to float + cvtdq2ps m1, m1 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + cvtdq2ps m4, m4 + cvtdq2ps m5, m5 + mulps m0, m7 ; scale float from s32 range to [-1.0,1.0] + mulps m1, m7 + mulps m2, m7 + mulps m3, m7 + mulps m4, m7 + mulps m5, m7 + mova [dstq ], m0 + mova [dstq+ mmsize], m1 + mova [dstq+2*mmsize], m2 + mova [dstq+3*mmsize], m3 + mova [dstq+4*mmsize], m4 + mova [dstq+5*mmsize], m5 + add srcq, mmsize/2 + add dstq, mmsize*6 + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16P_TO_FLT_6CH +INIT_XMM ssse3 +CONV_S16P_TO_FLT_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16P_TO_FLT_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_fltp_to_s16_2ch(int16_t *dst, float *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLTP_TO_S16_2CH 0 +cglobal conv_fltp_to_s16_2ch, 3,4,3, dst, src0, len, src1 + lea lenq, [4*lend] + mov src1q, [src0q+gprsize] + mov src0q, [src0q ] + add dstq, lenq + add src0q, lenq + add src1q, lenq + neg lenq + mova m2, [pf_s16_scale] +%if cpuflag(ssse3) + mova m3, [pb_interleave_words] +%endif +.loop: + mulps m0, m2, [src0q+lenq] ; m0 = 0, 2, 4, 6 + mulps m1, m2, [src1q+lenq] ; m1 = 1, 3, 5, 7 + cvtps2dq m0, m0 + cvtps2dq m1, m1 +%if cpuflag(ssse3) + packssdw m0, m1 ; m0 = 0, 2, 4, 6, 1, 3, 5, 7 + pshufb m0, m3 ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 +%else + packssdw m0, m0 ; m0 = 0, 2, 4, 6, x, x, x, x + packssdw m1, m1 ; m1 = 1, 3, 5, 7, x, x, x, x + punpcklwd m0, m1 ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 +%endif + mova [dstq+lenq], m0 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_FLTP_TO_S16_2CH +INIT_XMM ssse3 +CONV_FLTP_TO_S16_2CH + +;------------------------------------------------------------------------------ +; void ff_conv_fltp_to_s16_6ch(int16_t *dst, float *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLTP_TO_S16_6CH 0 +%if ARCH_X86_64 +cglobal conv_fltp_to_s16_6ch, 3,8,7, dst, src, len, src1, src2, src3, src4, src5 +%else +cglobal conv_fltp_to_s16_6ch, 2,7,7, dst, src, src1, src2, src3, src4, src5 +%define lend dword r2m +%endif + mov src1q, [srcq+1*gprsize] + mov src2q, [srcq+2*gprsize] + mov src3q, [srcq+3*gprsize] + mov src4q, [srcq+4*gprsize] + mov src5q, [srcq+5*gprsize] + mov srcq, [srcq] + sub src1q, srcq + sub src2q, srcq + sub src3q, srcq + sub src4q, srcq + sub src5q, srcq + movaps xmm6, [pf_s16_scale] +.loop: +%if cpuflag(sse2) + mulps m0, m6, [srcq ] + mulps m1, m6, [srcq+src1q] + mulps m2, m6, [srcq+src2q] + mulps m3, m6, [srcq+src3q] + mulps m4, m6, [srcq+src4q] + mulps m5, m6, [srcq+src5q] + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + cvtps2dq m4, m4 + cvtps2dq m5, m5 + packssdw m0, m3 ; m0 = 0, 6, 12, 18, 3, 9, 15, 21 + packssdw m1, m4 ; m1 = 1, 7, 13, 19, 4, 10, 16, 22 + packssdw m2, m5 ; m2 = 2, 8, 14, 20, 5, 11, 17, 23 + ; unpack words: + movhlps m3, m0 ; m3 = 3, 9, 15, 21, x, x, x, x + punpcklwd m0, m1 ; m0 = 0, 1, 6, 7, 12, 13, 18, 19 + punpckhwd m1, m2 ; m1 = 4, 5, 10, 11, 16, 17, 22, 23 + punpcklwd m2, m3 ; m2 = 2, 3, 8, 9, 14, 15, 20, 21 + ; blend dwords: + shufps m3, m0, m2, q2020 ; m3 = 0, 1, 12, 13, 2, 3, 14, 15 + shufps m0, m1, q2031 ; m0 = 6, 7, 18, 19, 4, 5, 16, 17 + shufps m2, m1, q3131 ; m2 = 8, 9, 20, 21, 10, 11, 22, 23 + ; shuffle dwords: + shufps m1, m2, m3, q3120 ; m1 = 8, 9, 10, 11, 12, 13, 14, 15 + shufps m3, m0, q0220 ; m3 = 0, 1, 2, 3, 4, 5, 6, 7 + shufps m0, m2, q3113 ; m0 = 16, 17, 18, 19, 20, 21, 22, 23 + mova [dstq+0*mmsize], m3 + mova [dstq+1*mmsize], m1 + mova [dstq+2*mmsize], m0 +%else ; sse + movlps xmm0, [srcq ] + movlps xmm1, [srcq+src1q] + movlps xmm2, [srcq+src2q] + movlps xmm3, [srcq+src3q] + movlps xmm4, [srcq+src4q] + movlps xmm5, [srcq+src5q] + mulps xmm0, xmm6 + mulps xmm1, xmm6 + mulps xmm2, xmm6 + mulps xmm3, xmm6 + mulps xmm4, xmm6 + mulps xmm5, xmm6 + cvtps2pi mm0, xmm0 + cvtps2pi mm1, xmm1 + cvtps2pi mm2, xmm2 + cvtps2pi mm3, xmm3 + cvtps2pi mm4, xmm4 + cvtps2pi mm5, xmm5 + packssdw mm0, mm3 ; m0 = 0, 6, 3, 9 + packssdw mm1, mm4 ; m1 = 1, 7, 4, 10 + packssdw mm2, mm5 ; m2 = 2, 8, 5, 11 + ; unpack words + pshufw mm3, mm0, q1032 ; m3 = 3, 9, 0, 6 + punpcklwd mm0, mm1 ; m0 = 0, 1, 6, 7 + punpckhwd mm1, mm2 ; m1 = 4, 5, 10, 11 + punpcklwd mm2, mm3 ; m2 = 2, 3, 8, 9 + ; unpack dwords + pshufw mm3, mm0, q1032 ; m3 = 6, 7, 0, 1 + punpckldq mm0, mm2 ; m0 = 0, 1, 2, 3 (final) + punpckhdq mm2, mm1 ; m2 = 8, 9, 10, 11 (final) + punpckldq mm1, mm3 ; m1 = 4, 5, 6, 7 (final) + mova [dstq+0*mmsize], mm0 + mova [dstq+1*mmsize], mm1 + mova [dstq+2*mmsize], mm2 +%endif + add srcq, mmsize + add dstq, mmsize*3 + sub lend, mmsize/4 + jg .loop +%if mmsize == 8 + emms + RET +%else + REP_RET +%endif +%endmacro + +INIT_MMX sse +CONV_FLTP_TO_S16_6CH +INIT_XMM sse2 +CONV_FLTP_TO_S16_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLTP_TO_S16_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_fltp_to_flt_2ch(float *dst, float *const *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLTP_TO_FLT_2CH 0 +cglobal conv_fltp_to_flt_2ch, 3,4,5, dst, src0, len, src1 + mov src1q, [src0q+gprsize] + mov src0q, [src0q] + lea lenq, [4*lend] + add src0q, lenq + add src1q, lenq + lea dstq, [dstq+2*lenq] + neg lenq +.loop: + mova m0, [src0q+lenq ] + mova m1, [src1q+lenq ] + mova m2, [src0q+lenq+mmsize] + mova m3, [src1q+lenq+mmsize] + SBUTTERFLYPS 0, 1, 4 + SBUTTERFLYPS 2, 3, 4 + mova [dstq+2*lenq+0*mmsize], m0 + mova [dstq+2*lenq+1*mmsize], m1 + mova [dstq+2*lenq+2*mmsize], m2 + mova [dstq+2*lenq+3*mmsize], m3 + add lenq, 2*mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse +CONV_FLTP_TO_FLT_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLTP_TO_FLT_2CH +%endif + +;----------------------------------------------------------------------------- +; void ff_conv_fltp_to_flt_6ch(float *dst, float *const *src, int len, +; int channels); +;----------------------------------------------------------------------------- + +%macro CONV_FLTP_TO_FLT_6CH 0 +cglobal conv_fltp_to_flt_6ch, 2,8,7, dst, src, src1, src2, src3, src4, src5, len +%if ARCH_X86_64 + mov lend, r2d +%else + %define lend dword r2m +%endif + mov src1q, [srcq+1*gprsize] + mov src2q, [srcq+2*gprsize] + mov src3q, [srcq+3*gprsize] + mov src4q, [srcq+4*gprsize] + mov src5q, [srcq+5*gprsize] + mov srcq, [srcq] + sub src1q, srcq + sub src2q, srcq + sub src3q, srcq + sub src4q, srcq + sub src5q, srcq +.loop: + mova m0, [srcq ] + mova m1, [srcq+src1q] + mova m2, [srcq+src2q] + mova m3, [srcq+src3q] + mova m4, [srcq+src4q] + mova m5, [srcq+src5q] +%if cpuflag(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 + + movaps [dstq ], m0 + movaps [dstq+16], m6 + movaps [dstq+32], m4 + movaps [dstq+48], m1 + movaps [dstq+64], m2 + movaps [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 + +INIT_MMX mmx +CONV_FLTP_TO_FLT_6CH +INIT_XMM sse4 +CONV_FLTP_TO_FLT_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLTP_TO_FLT_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_s16p_2ch(int16_t *const *dst, int16_t *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16_TO_S16P_2CH 0 +cglobal conv_s16_to_s16p_2ch, 3,4,4, dst0, src, len, dst1 + lea lenq, [2*lend] + mov dst1q, [dst0q+gprsize] + mov dst0q, [dst0q ] + lea srcq, [srcq+2*lenq] + add dst0q, lenq + add dst1q, lenq + neg lenq +%if cpuflag(ssse3) + mova m3, [pb_deinterleave_words] +%endif +.loop: + mova m0, [srcq+2*lenq ] ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 + mova m1, [srcq+2*lenq+mmsize] ; m1 = 8, 9, 10, 11, 12, 13, 14, 15 +%if cpuflag(ssse3) + pshufb m0, m3 ; m0 = 0, 2, 4, 6, 1, 3, 5, 7 + pshufb m1, m3 ; m1 = 8, 10, 12, 14, 9, 11, 13, 15 + SBUTTERFLY2 qdq, 0, 1, 2 ; m0 = 0, 2, 4, 6, 8, 10, 12, 14 + ; m1 = 1, 3, 5, 7, 9, 11, 13, 15 +%else ; sse2 + pshuflw m0, m0, q3120 ; m0 = 0, 2, 1, 3, 4, 5, 6, 7 + pshufhw m0, m0, q3120 ; m0 = 0, 2, 1, 3, 4, 6, 5, 7 + pshuflw m1, m1, q3120 ; m1 = 8, 10, 9, 11, 12, 13, 14, 15 + pshufhw m1, m1, q3120 ; m1 = 8, 10, 9, 11, 12, 14, 13, 15 + DEINT2_PS 0, 1, 2 ; m0 = 0, 2, 4, 6, 8, 10, 12, 14 + ; m1 = 1, 3, 5, 7, 9, 11, 13, 15 +%endif + mova [dst0q+lenq], m0 + mova [dst1q+lenq], m1 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16_TO_S16P_2CH +INIT_XMM ssse3 +CONV_S16_TO_S16P_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16_TO_S16P_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_s16p_6ch(int16_t *const *dst, int16_t *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16_TO_S16P_6CH 0 +%if ARCH_X86_64 +cglobal conv_s16_to_s16p_6ch, 3,8,5, dst, src, len, dst1, dst2, dst3, dst4, dst5 +%else +cglobal conv_s16_to_s16p_6ch, 2,7,5, dst, src, dst1, dst2, dst3, dst4, dst5 +%define lend dword r2m +%endif + mov dst1q, [dstq+ gprsize] + mov dst2q, [dstq+2*gprsize] + mov dst3q, [dstq+3*gprsize] + mov dst4q, [dstq+4*gprsize] + mov dst5q, [dstq+5*gprsize] + mov dstq, [dstq ] + sub dst1q, dstq + sub dst2q, dstq + sub dst3q, dstq + sub dst4q, dstq + sub dst5q, dstq +.loop: + mova m0, [srcq+0*mmsize] ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 + mova m3, [srcq+1*mmsize] ; m3 = 8, 9, 10, 11, 12, 13, 14, 15 + mova m2, [srcq+2*mmsize] ; m2 = 16, 17, 18, 19, 20, 21, 22, 23 + PALIGNR m1, m3, m0, 12, m4 ; m1 = 6, 7, 8, 9, 10, 11, x, x + shufps m3, m2, q1032 ; m3 = 12, 13, 14, 15, 16, 17, 18, 19 + psrldq m2, 4 ; m2 = 18, 19, 20, 21, 22, 23, x, x + SBUTTERFLY2 wd, 0, 1, 4 ; m0 = 0, 6, 1, 7, 2, 8, 3, 9 + ; m1 = 4, 10, 5, 11, x, x, x, x + SBUTTERFLY2 wd, 3, 2, 4 ; m3 = 12, 18, 13, 19, 14, 20, 15, 21 + ; m2 = 16, 22, 17, 23, x, x, x, x + SBUTTERFLY2 dq, 0, 3, 4 ; m0 = 0, 6, 12, 18, 1, 7, 13, 19 + ; m3 = 2, 8, 14, 20, 3, 9, 15, 21 + punpckldq m1, m2 ; m1 = 4, 10, 16, 22, 5, 11, 17, 23 + movq [dstq ], m0 + movhps [dstq+dst1q], m0 + movq [dstq+dst2q], m3 + movhps [dstq+dst3q], m3 + movq [dstq+dst4q], m1 + movhps [dstq+dst5q], m1 + add srcq, mmsize*3 + add dstq, mmsize/2 + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16_TO_S16P_6CH +INIT_XMM ssse3 +CONV_S16_TO_S16P_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16_TO_S16P_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_fltp_2ch(float *const *dst, int16_t *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16_TO_FLTP_2CH 0 +cglobal conv_s16_to_fltp_2ch, 3,4,5, dst0, src, len, dst1 + lea lenq, [4*lend] + mov dst1q, [dst0q+gprsize] + mov dst0q, [dst0q ] + add srcq, lenq + add dst0q, lenq + add dst1q, lenq + neg lenq + mova m3, [pf_s32_inv_scale] + mova m4, [pw_zero_even] +.loop: + mova m1, [srcq+lenq] + pslld m0, m1, 16 + pand m1, m4 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + mulps m0, m0, m3 + mulps m1, m1, m3 + mova [dst0q+lenq], m0 + mova [dst1q+lenq], m1 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16_TO_FLTP_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16_TO_FLTP_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_s16_to_fltp_6ch(float *const *dst, int16_t *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_S16_TO_FLTP_6CH 0 +%if ARCH_X86_64 +cglobal conv_s16_to_fltp_6ch, 3,8,7, dst, src, len, dst1, dst2, dst3, dst4, dst5 +%else +cglobal conv_s16_to_fltp_6ch, 2,7,7, dst, src, dst1, dst2, dst3, dst4, dst5 +%define lend dword r2m +%endif + mov dst1q, [dstq+ gprsize] + mov dst2q, [dstq+2*gprsize] + mov dst3q, [dstq+3*gprsize] + mov dst4q, [dstq+4*gprsize] + mov dst5q, [dstq+5*gprsize] + mov dstq, [dstq ] + sub dst1q, dstq + sub dst2q, dstq + sub dst3q, dstq + sub dst4q, dstq + sub dst5q, dstq + mova m6, [pf_s16_inv_scale] +.loop: + mova m0, [srcq+0*mmsize] ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 + mova m3, [srcq+1*mmsize] ; m3 = 8, 9, 10, 11, 12, 13, 14, 15 + mova m2, [srcq+2*mmsize] ; m2 = 16, 17, 18, 19, 20, 21, 22, 23 + PALIGNR m1, m3, m0, 12, m4 ; m1 = 6, 7, 8, 9, 10, 11, x, x + shufps m3, m2, q1032 ; m3 = 12, 13, 14, 15, 16, 17, 18, 19 + psrldq m2, 4 ; m2 = 18, 19, 20, 21, 22, 23, x, x + SBUTTERFLY2 wd, 0, 1, 4 ; m0 = 0, 6, 1, 7, 2, 8, 3, 9 + ; m1 = 4, 10, 5, 11, x, x, x, x + SBUTTERFLY2 wd, 3, 2, 4 ; m3 = 12, 18, 13, 19, 14, 20, 15, 21 + ; m2 = 16, 22, 17, 23, x, x, x, x + SBUTTERFLY2 dq, 0, 3, 4 ; m0 = 0, 6, 12, 18, 1, 7, 13, 19 + ; m3 = 2, 8, 14, 20, 3, 9, 15, 21 + punpckldq m1, m2 ; m1 = 4, 10, 16, 22, 5, 11, 17, 23 + S16_TO_S32_SX 0, 2 ; m0 = 0, 6, 12, 18 + ; m2 = 1, 7, 13, 19 + S16_TO_S32_SX 3, 4 ; m3 = 2, 8, 14, 20 + ; m4 = 3, 9, 15, 21 + S16_TO_S32_SX 1, 5 ; m1 = 4, 10, 16, 22 + ; m5 = 5, 11, 17, 23 + SWAP 1,2,3,4 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + cvtdq2ps m4, m4 + cvtdq2ps m5, m5 + mulps m0, m6 + mulps m1, m6 + mulps m2, m6 + mulps m3, m6 + mulps m4, m6 + mulps m5, m6 + mova [dstq ], m0 + mova [dstq+dst1q], m1 + mova [dstq+dst2q], m2 + mova [dstq+dst3q], m3 + mova [dstq+dst4q], m4 + mova [dstq+dst5q], m5 + add srcq, mmsize*3 + add dstq, mmsize + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_S16_TO_FLTP_6CH +INIT_XMM ssse3 +CONV_S16_TO_FLTP_6CH +INIT_XMM sse4 +CONV_S16_TO_FLTP_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_S16_TO_FLTP_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_s16p_2ch(int16_t *const *dst, float *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLT_TO_S16P_2CH 0 +cglobal conv_flt_to_s16p_2ch, 3,4,6, dst0, src, len, dst1 + lea lenq, [2*lend] + mov dst1q, [dst0q+gprsize] + mov dst0q, [dst0q ] + lea srcq, [srcq+4*lenq] + add dst0q, lenq + add dst1q, lenq + neg lenq + mova m5, [pf_s16_scale] +.loop: + mova m0, [srcq+4*lenq ] + mova m1, [srcq+4*lenq+ mmsize] + mova m2, [srcq+4*lenq+2*mmsize] + mova m3, [srcq+4*lenq+3*mmsize] + DEINT2_PS 0, 1, 4 + DEINT2_PS 2, 3, 4 + mulps m0, m0, m5 + mulps m1, m1, m5 + mulps m2, m2, m5 + mulps m3, m3, m5 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + packssdw m0, m2 + packssdw m1, m3 + mova [dst0q+lenq], m0 + mova [dst1q+lenq], m1 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_FLT_TO_S16P_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLT_TO_S16P_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_s16p_6ch(int16_t *const *dst, float *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLT_TO_S16P_6CH 0 +%if ARCH_X86_64 +cglobal conv_flt_to_s16p_6ch, 3,8,7, dst, src, len, dst1, dst2, dst3, dst4, dst5 +%else +cglobal conv_flt_to_s16p_6ch, 2,7,7, dst, src, dst1, dst2, dst3, dst4, dst5 +%define lend dword r2m +%endif + mov dst1q, [dstq+ gprsize] + mov dst2q, [dstq+2*gprsize] + mov dst3q, [dstq+3*gprsize] + mov dst4q, [dstq+4*gprsize] + mov dst5q, [dstq+5*gprsize] + mov dstq, [dstq ] + sub dst1q, dstq + sub dst2q, dstq + sub dst3q, dstq + sub dst4q, dstq + sub dst5q, dstq + mova m6, [pf_s16_scale] +.loop: + mulps m0, m6, [srcq+0*mmsize] + mulps m3, m6, [srcq+1*mmsize] + mulps m1, m6, [srcq+2*mmsize] + mulps m4, m6, [srcq+3*mmsize] + mulps m2, m6, [srcq+4*mmsize] + mulps m5, m6, [srcq+5*mmsize] + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + cvtps2dq m4, m4 + cvtps2dq m5, m5 + packssdw m0, m3 ; m0 = 0, 1, 2, 3, 4, 5, 6, 7 + packssdw m1, m4 ; m1 = 8, 9, 10, 11, 12, 13, 14, 15 + packssdw m2, m5 ; m2 = 16, 17, 18, 19, 20, 21, 22, 23 + PALIGNR m3, m1, m0, 12, m4 ; m3 = 6, 7, 8, 9, 10, 11, x, x + shufps m1, m2, q1032 ; m1 = 12, 13, 14, 15, 16, 17, 18, 19 + psrldq m2, 4 ; m2 = 18, 19, 20, 21, 22, 23, x, x + SBUTTERFLY2 wd, 0, 3, 4 ; m0 = 0, 6, 1, 7, 2, 8, 3, 9 + ; m3 = 4, 10, 5, 11, x, x, x, x + SBUTTERFLY2 wd, 1, 2, 4 ; m1 = 12, 18, 13, 19, 14, 20, 15, 21 + ; m2 = 16, 22, 17, 23, x, x, x, x + SBUTTERFLY2 dq, 0, 1, 4 ; m0 = 0, 6, 12, 18, 1, 7, 13, 19 + ; m1 = 2, 8, 14, 20, 3, 9, 15, 21 + punpckldq m3, m2 ; m3 = 4, 10, 16, 22, 5, 11, 17, 23 + movq [dstq ], m0 + movhps [dstq+dst1q], m0 + movq [dstq+dst2q], m1 + movhps [dstq+dst3q], m1 + movq [dstq+dst4q], m3 + movhps [dstq+dst5q], m3 + add srcq, mmsize*6 + add dstq, mmsize/2 + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_FLT_TO_S16P_6CH +INIT_XMM ssse3 +CONV_FLT_TO_S16P_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLT_TO_S16P_6CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_fltp_2ch(float *const *dst, float *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLT_TO_FLTP_2CH 0 +cglobal conv_flt_to_fltp_2ch, 3,4,3, dst0, src, len, dst1 + lea lenq, [4*lend] + mov dst1q, [dst0q+gprsize] + mov dst0q, [dst0q ] + lea srcq, [srcq+2*lenq] + add dst0q, lenq + add dst1q, lenq + neg lenq +.loop: + mova m0, [srcq+2*lenq ] + mova m1, [srcq+2*lenq+mmsize] + DEINT2_PS 0, 1, 2 + mova [dst0q+lenq], m0 + mova [dst1q+lenq], m1 + add lenq, mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse +CONV_FLT_TO_FLTP_2CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLT_TO_FLTP_2CH +%endif + +;------------------------------------------------------------------------------ +; void ff_conv_flt_to_fltp_6ch(float *const *dst, float *src, int len, +; int channels); +;------------------------------------------------------------------------------ + +%macro CONV_FLT_TO_FLTP_6CH 0 +%if ARCH_X86_64 +cglobal conv_flt_to_fltp_6ch, 3,8,7, dst, src, len, dst1, dst2, dst3, dst4, dst5 +%else +cglobal conv_flt_to_fltp_6ch, 2,7,7, dst, src, dst1, dst2, dst3, dst4, dst5 +%define lend dword r2m +%endif + mov dst1q, [dstq+ gprsize] + mov dst2q, [dstq+2*gprsize] + mov dst3q, [dstq+3*gprsize] + mov dst4q, [dstq+4*gprsize] + mov dst5q, [dstq+5*gprsize] + mov dstq, [dstq ] + sub dst1q, dstq + sub dst2q, dstq + sub dst3q, dstq + sub dst4q, dstq + sub dst5q, dstq +.loop: + mova m0, [srcq+0*mmsize] ; m0 = 0, 1, 2, 3 + mova m1, [srcq+1*mmsize] ; m1 = 4, 5, 6, 7 + mova m2, [srcq+2*mmsize] ; m2 = 8, 9, 10, 11 + mova m3, [srcq+3*mmsize] ; m3 = 12, 13, 14, 15 + mova m4, [srcq+4*mmsize] ; m4 = 16, 17, 18, 19 + mova m5, [srcq+5*mmsize] ; m5 = 20, 21, 22, 23 + + SBUTTERFLY2 dq, 0, 3, 6 ; m0 = 0, 12, 1, 13 + ; m3 = 2, 14, 3, 15 + SBUTTERFLY2 dq, 1, 4, 6 ; m1 = 4, 16, 5, 17 + ; m4 = 6, 18, 7, 19 + SBUTTERFLY2 dq, 2, 5, 6 ; m2 = 8, 20, 9, 21 + ; m5 = 10, 22, 11, 23 + SBUTTERFLY2 dq, 0, 4, 6 ; m0 = 0, 6, 12, 18 + ; m4 = 1, 7, 13, 19 + SBUTTERFLY2 dq, 3, 2, 6 ; m3 = 2, 8, 14, 20 + ; m2 = 3, 9, 15, 21 + SBUTTERFLY2 dq, 1, 5, 6 ; m1 = 4, 10, 16, 22 + ; m5 = 5, 11, 17, 23 + mova [dstq ], m0 + mova [dstq+dst1q], m4 + mova [dstq+dst2q], m3 + mova [dstq+dst3q], m2 + mova [dstq+dst4q], m1 + mova [dstq+dst5q], m5 + add srcq, mmsize*6 + add dstq, mmsize + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +CONV_FLT_TO_FLTP_6CH +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +CONV_FLT_TO_FLTP_6CH +%endif diff --git a/lib/ffmpeg/libavresample/x86/audio_convert_init.c b/lib/ffmpeg/libavresample/x86/audio_convert_init.c new file mode 100644 index 0000000000..879108dff1 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/audio_convert_init.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "libavutil/cpu.h" +#include "libavutil/x86/cpu.h" +#include "libavresample/audio_convert.h" + +/* flat conversions */ + +extern void ff_conv_s16_to_s32_sse2(int16_t *dst, const int32_t *src, int len); + +extern void ff_conv_s16_to_flt_sse2(float *dst, const int16_t *src, int len); +extern void ff_conv_s16_to_flt_sse4(float *dst, const int16_t *src, int len); + +extern void ff_conv_s32_to_s16_mmx (int16_t *dst, const int32_t *src, int len); +extern void ff_conv_s32_to_s16_sse2(int16_t *dst, const int32_t *src, int len); + +extern void ff_conv_s32_to_flt_sse2(float *dst, const int32_t *src, int len); +extern void ff_conv_s32_to_flt_avx (float *dst, const int32_t *src, int len); + +extern void ff_conv_flt_to_s16_sse2(int16_t *dst, const float *src, int len); + +extern void ff_conv_flt_to_s32_sse2(int32_t *dst, const float *src, int len); +extern void ff_conv_flt_to_s32_avx (int32_t *dst, const float *src, int len); + +/* interleave conversions */ + +extern void ff_conv_s16p_to_s16_2ch_sse2(int16_t *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_s16_2ch_avx (int16_t *dst, int16_t *const *src, + int len, int channels); + +extern void ff_conv_s16p_to_s16_6ch_sse2(int16_t *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_s16_6ch_sse2slow(int16_t *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_s16_6ch_avx (int16_t *dst, int16_t *const *src, + int len, int channels); + +extern void ff_conv_s16p_to_flt_2ch_sse2(float *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_flt_2ch_avx (float *dst, int16_t *const *src, + int len, int channels); + +extern void ff_conv_s16p_to_flt_6ch_sse2 (float *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_flt_6ch_ssse3(float *dst, int16_t *const *src, + int len, int channels); +extern void ff_conv_s16p_to_flt_6ch_avx (float *dst, int16_t *const *src, + int len, int channels); + +extern void ff_conv_fltp_to_s16_2ch_sse2 (int16_t *dst, float *const *src, + int len, int channels); +extern void ff_conv_fltp_to_s16_2ch_ssse3(int16_t *dst, float *const *src, + int len, int channels); + +extern void ff_conv_fltp_to_s16_6ch_sse (int16_t *dst, float *const *src, + int len, int channels); +extern void ff_conv_fltp_to_s16_6ch_sse2(int16_t *dst, float *const *src, + int len, int channels); +extern void ff_conv_fltp_to_s16_6ch_avx (int16_t *dst, float *const *src, + int len, int channels); + +extern void ff_conv_fltp_to_flt_2ch_sse(float *dst, float *const *src, int len, + int channels); +extern void ff_conv_fltp_to_flt_2ch_avx(float *dst, float *const *src, int len, + int channels); + +extern void ff_conv_fltp_to_flt_6ch_mmx (float *dst, float *const *src, int len, + int channels); +extern void ff_conv_fltp_to_flt_6ch_sse4(float *dst, float *const *src, int len, + int channels); +extern void ff_conv_fltp_to_flt_6ch_avx (float *dst, float *const *src, int len, + int channels); + +/* deinterleave conversions */ + +extern void ff_conv_s16_to_s16p_2ch_sse2(int16_t *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_s16p_2ch_ssse3(int16_t *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_s16p_2ch_avx (int16_t *const *dst, int16_t *src, + int len, int channels); + +extern void ff_conv_s16_to_s16p_6ch_sse2 (int16_t *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_s16p_6ch_ssse3(int16_t *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_s16p_6ch_avx (int16_t *const *dst, int16_t *src, + int len, int channels); + +extern void ff_conv_s16_to_fltp_2ch_sse2(float *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_fltp_2ch_avx (float *const *dst, int16_t *src, + int len, int channels); + +extern void ff_conv_s16_to_fltp_6ch_sse2 (float *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_fltp_6ch_ssse3(float *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_fltp_6ch_sse4 (float *const *dst, int16_t *src, + int len, int channels); +extern void ff_conv_s16_to_fltp_6ch_avx (float *const *dst, int16_t *src, + int len, int channels); + +extern void ff_conv_flt_to_s16p_2ch_sse2(int16_t *const *dst, float *src, + int len, int channels); +extern void ff_conv_flt_to_s16p_2ch_avx (int16_t *const *dst, float *src, + int len, int channels); + +extern void ff_conv_flt_to_s16p_6ch_sse2 (int16_t *const *dst, float *src, + int len, int channels); +extern void ff_conv_flt_to_s16p_6ch_ssse3(int16_t *const *dst, float *src, + int len, int channels); +extern void ff_conv_flt_to_s16p_6ch_avx (int16_t *const *dst, float *src, + int len, int channels); + +extern void ff_conv_flt_to_fltp_2ch_sse(float *const *dst, float *src, int len, + int channels); +extern void ff_conv_flt_to_fltp_2ch_avx(float *const *dst, float *src, int len, + int channels); + +extern void ff_conv_flt_to_fltp_6ch_sse2(float *const *dst, float *src, int len, + int channels); +extern void ff_conv_flt_to_fltp_6ch_avx (float *const *dst, float *src, int len, + int channels); + +av_cold void ff_audio_convert_init_x86(AudioConvert *ac) +{ + int mm_flags = av_get_cpu_flags(); + + if (EXTERNAL_MMX(mm_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, + 0, 1, 8, "MMX", ff_conv_s32_to_s16_mmx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 6, 1, 4, "MMX", ff_conv_fltp_to_flt_6ch_mmx); + } + if (EXTERNAL_SSE(mm_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 6, 1, 2, "SSE", ff_conv_fltp_to_s16_6ch_sse); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 2, 16, 8, "SSE", ff_conv_fltp_to_flt_2ch_sse); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_FLT, + 2, 16, 4, "SSE", ff_conv_flt_to_fltp_2ch_sse); + } + if (EXTERNAL_SSE2(mm_flags)) { + if (!(mm_flags & AV_CPU_FLAG_SSE2SLOW)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, + 0, 16, 16, "SSE2", ff_conv_s32_to_s16_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + 6, 16, 8, "SSE2", ff_conv_s16p_to_s16_6ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 6, 16, 4, "SSE2", ff_conv_fltp_to_s16_6ch_sse2); + } else { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + 6, 1, 4, "SSE2SLOW", ff_conv_s16p_to_s16_6ch_sse2slow); + } + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S16, + 0, 16, 8, "SSE2", ff_conv_s16_to_s32_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16, + 0, 16, 8, "SSE2", ff_conv_s16_to_flt_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32, + 0, 16, 8, "SSE2", ff_conv_s32_to_flt_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLT, + 0, 16, 16, "SSE2", ff_conv_flt_to_s16_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT, + 0, 16, 16, "SSE2", ff_conv_flt_to_s32_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + 2, 16, 16, "SSE2", ff_conv_s16p_to_s16_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16P, + 2, 16, 8, "SSE2", ff_conv_s16p_to_flt_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16P, + 6, 16, 4, "SSE2", ff_conv_s16p_to_flt_6ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 2, 16, 4, "SSE2", ff_conv_fltp_to_s16_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 2, 16, 8, "SSE2", ff_conv_s16_to_s16p_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 6, 16, 4, "SSE2", ff_conv_s16_to_s16p_6ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16, + 2, 16, 8, "SSE2", ff_conv_s16_to_fltp_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16, + 6, 16, 4, "SSE2", ff_conv_s16_to_fltp_6ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLT, + 2, 16, 8, "SSE2", ff_conv_flt_to_s16p_2ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLT, + 6, 16, 4, "SSE2", ff_conv_flt_to_s16p_6ch_sse2); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_FLT, + 6, 16, 4, "SSE2", ff_conv_flt_to_fltp_6ch_sse2); + } + if (EXTERNAL_SSSE3(mm_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16P, + 6, 16, 4, "SSSE3", ff_conv_s16p_to_flt_6ch_ssse3); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 2, 16, 4, "SSSE3", ff_conv_fltp_to_s16_2ch_ssse3); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 2, 16, 8, "SSSE3", ff_conv_s16_to_s16p_2ch_ssse3); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 6, 16, 4, "SSSE3", ff_conv_s16_to_s16p_6ch_ssse3); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16, + 6, 16, 4, "SSSE3", ff_conv_s16_to_fltp_6ch_ssse3); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLT, + 6, 16, 4, "SSSE3", ff_conv_flt_to_s16p_6ch_ssse3); + } + if (EXTERNAL_SSE4(mm_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16, + 0, 16, 8, "SSE4", ff_conv_s16_to_flt_sse4); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 6, 16, 4, "SSE4", ff_conv_fltp_to_flt_6ch_sse4); + } + if (EXTERNAL_AVX(mm_flags)) { + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S32, + 0, 32, 16, "AVX", ff_conv_s32_to_flt_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_FLT, + 0, 32, 32, "AVX", ff_conv_flt_to_s32_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + 2, 16, 16, "AVX", ff_conv_s16p_to_s16_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P, + 6, 16, 8, "AVX", ff_conv_s16p_to_s16_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16P, + 2, 16, 8, "AVX", ff_conv_s16p_to_flt_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16P, + 6, 16, 4, "AVX", ff_conv_s16p_to_flt_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_FLTP, + 6, 16, 4, "AVX", ff_conv_fltp_to_s16_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, + 6, 16, 4, "AVX", ff_conv_fltp_to_flt_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 2, 16, 8, "AVX", ff_conv_s16_to_s16p_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S16, + 6, 16, 4, "AVX", ff_conv_s16_to_s16p_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16, + 2, 16, 8, "AVX", ff_conv_s16_to_fltp_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S16, + 6, 16, 4, "AVX", ff_conv_s16_to_fltp_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLT, + 2, 16, 8, "AVX", ff_conv_flt_to_s16p_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLT, + 6, 16, 4, "AVX", ff_conv_flt_to_s16p_6ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_FLT, + 2, 16, 4, "AVX", ff_conv_flt_to_fltp_2ch_avx); + ff_audio_convert_set_func(ac, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_FLT, + 6, 16, 4, "AVX", ff_conv_flt_to_fltp_6ch_avx); + } +} diff --git a/lib/ffmpeg/libavresample/x86/audio_mix.asm b/lib/ffmpeg/libavresample/x86/audio_mix.asm new file mode 100644 index 0000000000..9353593002 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/audio_mix.asm @@ -0,0 +1,511 @@ +;****************************************************************************** +;* x86 optimized channel mixing +;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> +;* +;* This file is part of Libav. +;* +;* Libav is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* Libav is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General Public +;* License along with Libav; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%include "libavutil/x86/x86util.asm" +%include "util.asm" + +SECTION_TEXT + +;----------------------------------------------------------------------------- +; void ff_mix_2_to_1_fltp_flt(float **src, float **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_2_TO_1_FLTP_FLT 0 +cglobal mix_2_to_1_fltp_flt, 3,4,6, src, matrix, len, src1 + mov src1q, [srcq+gprsize] + mov srcq, [srcq ] + sub src1q, srcq + mov matrixq, [matrixq ] + VBROADCASTSS m4, [matrixq ] + VBROADCASTSS m5, [matrixq+4] + ALIGN 16 +.loop: + mulps m0, m4, [srcq ] + mulps m1, m5, [srcq+src1q ] + mulps m2, m4, [srcq+ mmsize] + mulps m3, m5, [srcq+src1q+mmsize] + addps m0, m0, m1 + addps m2, m2, m3 + mova [srcq ], m0 + mova [srcq+mmsize], m2 + add srcq, mmsize*2 + sub lend, mmsize*2/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse +MIX_2_TO_1_FLTP_FLT +%if HAVE_AVX_EXTERNAL +INIT_YMM avx +MIX_2_TO_1_FLTP_FLT +%endif + +;----------------------------------------------------------------------------- +; void ff_mix_2_to_1_s16p_flt(int16_t **src, float **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_2_TO_1_S16P_FLT 0 +cglobal mix_2_to_1_s16p_flt, 3,4,6, src, matrix, len, src1 + mov src1q, [srcq+gprsize] + mov srcq, [srcq] + sub src1q, srcq + mov matrixq, [matrixq ] + VBROADCASTSS m4, [matrixq ] + VBROADCASTSS m5, [matrixq+4] + ALIGN 16 +.loop: + mova m0, [srcq ] + mova m2, [srcq+src1q] + S16_TO_S32_SX 0, 1 + S16_TO_S32_SX 2, 3 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + mulps m0, m4 + mulps m1, m4 + mulps m2, m5 + mulps m3, m5 + addps m0, m2 + addps m1, m3 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + packssdw m0, m1 + mova [srcq], m0 + add srcq, mmsize + sub lend, mmsize/2 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +MIX_2_TO_1_S16P_FLT +INIT_XMM sse4 +MIX_2_TO_1_S16P_FLT + +;----------------------------------------------------------------------------- +; void ff_mix_2_to_1_s16p_q8(int16_t **src, int16_t **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +INIT_XMM sse2 +cglobal mix_2_to_1_s16p_q8, 3,4,6, src, matrix, len, src1 + mov src1q, [srcq+gprsize] + mov srcq, [srcq] + sub src1q, srcq + mov matrixq, [matrixq] + movd m4, [matrixq] + movd m5, [matrixq] + SPLATW m4, m4, 0 + SPLATW m5, m5, 1 + pxor m0, m0 + punpcklwd m4, m0 + punpcklwd m5, m0 + ALIGN 16 +.loop: + mova m0, [srcq ] + mova m2, [srcq+src1q] + punpckhwd m1, m0, m0 + punpcklwd m0, m0 + punpckhwd m3, m2, m2 + punpcklwd m2, m2 + pmaddwd m0, m4 + pmaddwd m1, m4 + pmaddwd m2, m5 + pmaddwd m3, m5 + paddd m0, m2 + paddd m1, m3 + psrad m0, 8 + psrad m1, 8 + packssdw m0, m1 + mova [srcq], m0 + add srcq, mmsize + sub lend, mmsize/2 + jg .loop + REP_RET + +;----------------------------------------------------------------------------- +; void ff_mix_1_to_2_fltp_flt(float **src, float **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_1_TO_2_FLTP_FLT 0 +cglobal mix_1_to_2_fltp_flt, 3,5,4, src0, matrix0, len, src1, matrix1 + mov src1q, [src0q+gprsize] + mov src0q, [src0q] + sub src1q, src0q + mov matrix1q, [matrix0q+gprsize] + mov matrix0q, [matrix0q] + VBROADCASTSS m2, [matrix0q] + VBROADCASTSS m3, [matrix1q] + ALIGN 16 +.loop: + mova m0, [src0q] + mulps m1, m0, m3 + mulps m0, m0, m2 + mova [src0q ], m0 + mova [src0q+src1q], m1 + add src0q, mmsize + sub lend, mmsize/4 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse +MIX_1_TO_2_FLTP_FLT +%if HAVE_AVX_EXTERNAL +INIT_YMM avx +MIX_1_TO_2_FLTP_FLT +%endif + +;----------------------------------------------------------------------------- +; void ff_mix_1_to_2_s16p_flt(int16_t **src, float **matrix, int len, +; int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_1_TO_2_S16P_FLT 0 +cglobal mix_1_to_2_s16p_flt, 3,5,6, src0, matrix0, len, src1, matrix1 + mov src1q, [src0q+gprsize] + mov src0q, [src0q] + sub src1q, src0q + mov matrix1q, [matrix0q+gprsize] + mov matrix0q, [matrix0q] + VBROADCASTSS m4, [matrix0q] + VBROADCASTSS m5, [matrix1q] + ALIGN 16 +.loop: + mova m0, [src0q] + S16_TO_S32_SX 0, 2 + cvtdq2ps m0, m0 + cvtdq2ps m2, m2 + mulps m1, m0, m5 + mulps m0, m0, m4 + mulps m3, m2, m5 + mulps m2, m2, m4 + cvtps2dq m0, m0 + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + packssdw m0, m2 + packssdw m1, m3 + mova [src0q ], m0 + mova [src0q+src1q], m1 + add src0q, mmsize + sub lend, mmsize/2 + jg .loop + REP_RET +%endmacro + +INIT_XMM sse2 +MIX_1_TO_2_S16P_FLT +INIT_XMM sse4 +MIX_1_TO_2_S16P_FLT +%if HAVE_AVX_EXTERNAL +INIT_XMM avx +MIX_1_TO_2_S16P_FLT +%endif + +;----------------------------------------------------------------------------- +; void ff_mix_3_8_to_1_2_fltp/s16p_flt(float/int16_t **src, float **matrix, +; int len, int out_ch, int in_ch); +;----------------------------------------------------------------------------- + +%macro MIX_3_8_TO_1_2_FLT 3 ; %1 = in channels, %2 = out channels, %3 = s16p or fltp +; define some names to make the code clearer +%assign in_channels %1 +%assign out_channels %2 +%assign stereo out_channels - 1 +%ifidn %3, s16p + %assign is_s16 1 +%else + %assign is_s16 0 +%endif + +; determine how many matrix elements must go on the stack vs. mmregs +%assign matrix_elements in_channels * out_channels +%if is_s16 + %if stereo + %assign needed_mmregs 7 + %else + %assign needed_mmregs 5 + %endif +%else + %if stereo + %assign needed_mmregs 4 + %else + %assign needed_mmregs 3 + %endif +%endif +%assign matrix_elements_mm num_mmregs - needed_mmregs +%if matrix_elements < matrix_elements_mm + %assign matrix_elements_mm matrix_elements +%endif +%if matrix_elements_mm < matrix_elements + %assign matrix_elements_stack matrix_elements - matrix_elements_mm +%else + %assign matrix_elements_stack 0 +%endif +%assign matrix_stack_size matrix_elements_stack * mmsize + +%assign needed_stack_size -1 * matrix_stack_size +%if ARCH_X86_32 && in_channels >= 7 +%assign needed_stack_size needed_stack_size - 16 +%endif + +cglobal mix_%1_to_%2_%3_flt, 3,in_channels+2,needed_mmregs+matrix_elements_mm, needed_stack_size, src0, src1, len, src2, src3, src4, src5, src6, src7 + +; define src pointers on stack if needed +%if matrix_elements_stack > 0 && ARCH_X86_32 && in_channels >= 7 + %define src5m [rsp+matrix_stack_size+0] + %define src6m [rsp+matrix_stack_size+4] + %define src7m [rsp+matrix_stack_size+8] +%endif + +; load matrix pointers +%define matrix0q r1q +%define matrix1q r3q +%if stereo + mov matrix1q, [matrix0q+gprsize] +%endif + mov matrix0q, [matrix0q] + +; define matrix coeff names +%assign %%i 0 +%assign %%j needed_mmregs +%rep in_channels + %if %%i >= matrix_elements_mm + CAT_XDEFINE mx_stack_0_, %%i, 1 + CAT_XDEFINE mx_0_, %%i, [rsp+(%%i-matrix_elements_mm)*mmsize] + %else + CAT_XDEFINE mx_stack_0_, %%i, 0 + CAT_XDEFINE mx_0_, %%i, m %+ %%j + %assign %%j %%j+1 + %endif + %assign %%i %%i+1 +%endrep +%if stereo +%assign %%i 0 +%rep in_channels + %if in_channels + %%i >= matrix_elements_mm + CAT_XDEFINE mx_stack_1_, %%i, 1 + CAT_XDEFINE mx_1_, %%i, [rsp+(in_channels+%%i-matrix_elements_mm)*mmsize] + %else + CAT_XDEFINE mx_stack_1_, %%i, 0 + CAT_XDEFINE mx_1_, %%i, m %+ %%j + %assign %%j %%j+1 + %endif + %assign %%i %%i+1 +%endrep +%endif + +; load/splat matrix coeffs +%assign %%i 0 +%rep in_channels + %if mx_stack_0_ %+ %%i + VBROADCASTSS m0, [matrix0q+4*%%i] + mova mx_0_ %+ %%i, m0 + %else + VBROADCASTSS mx_0_ %+ %%i, [matrix0q+4*%%i] + %endif + %if stereo + %if mx_stack_1_ %+ %%i + VBROADCASTSS m0, [matrix1q+4*%%i] + mova mx_1_ %+ %%i, m0 + %else + VBROADCASTSS mx_1_ %+ %%i, [matrix1q+4*%%i] + %endif + %endif + %assign %%i %%i+1 +%endrep + +; load channel pointers to registers as offsets from the first channel pointer +%if ARCH_X86_64 + movsxd lenq, r2d +%endif + shl lenq, 2-is_s16 +%assign %%i 1 +%rep (in_channels - 1) + %if ARCH_X86_32 && in_channels >= 7 && %%i >= 5 + mov src5q, [src0q+%%i*gprsize] + add src5q, lenq + mov src %+ %%i %+ m, src5q + %else + mov src %+ %%i %+ q, [src0q+%%i*gprsize] + add src %+ %%i %+ q, lenq + %endif + %assign %%i %%i+1 +%endrep + mov src0q, [src0q] + add src0q, lenq + neg lenq +.loop: +; for x86-32 with 7-8 channels we do not have enough gp registers for all src +; pointers, so we have to load some of them from the stack each time +%define copy_src_from_stack ARCH_X86_32 && in_channels >= 7 && %%i >= 5 +%if is_s16 + ; mix with s16p input + mova m0, [src0q+lenq] + S16_TO_S32_SX 0, 1 + cvtdq2ps m0, m0 + cvtdq2ps m1, m1 + %if stereo + mulps m2, m0, mx_1_0 + mulps m3, m1, mx_1_0 + %endif + mulps m0, m0, mx_0_0 + mulps m1, m1, mx_0_0 +%assign %%i 1 +%rep (in_channels - 1) + %if copy_src_from_stack + %define src_ptr src5q + %else + %define src_ptr src %+ %%i %+ q + %endif + %if stereo + %if copy_src_from_stack + mov src_ptr, src %+ %%i %+ m + %endif + mova m4, [src_ptr+lenq] + S16_TO_S32_SX 4, 5 + cvtdq2ps m4, m4 + cvtdq2ps m5, m5 + fmaddps m2, m4, mx_1_ %+ %%i, m2, m6 + fmaddps m3, m5, mx_1_ %+ %%i, m3, m6 + fmaddps m0, m4, mx_0_ %+ %%i, m0, m4 + fmaddps m1, m5, mx_0_ %+ %%i, m1, m5 + %else + %if copy_src_from_stack + mov src_ptr, src %+ %%i %+ m + %endif + mova m2, [src_ptr+lenq] + S16_TO_S32_SX 2, 3 + cvtdq2ps m2, m2 + cvtdq2ps m3, m3 + fmaddps m0, m2, mx_0_ %+ %%i, m0, m4 + fmaddps m1, m3, mx_0_ %+ %%i, m1, m4 + %endif + %assign %%i %%i+1 +%endrep + %if stereo + cvtps2dq m2, m2 + cvtps2dq m3, m3 + packssdw m2, m3 + mova [src1q+lenq], m2 + %endif + cvtps2dq m0, m0 + cvtps2dq m1, m1 + packssdw m0, m1 + mova [src0q+lenq], m0 +%else + ; mix with fltp input + %if stereo || mx_stack_0_0 + mova m0, [src0q+lenq] + %endif + %if stereo + mulps m1, m0, mx_1_0 + %endif + %if stereo || mx_stack_0_0 + mulps m0, m0, mx_0_0 + %else + mulps m0, [src0q+lenq], mx_0_0 + %endif +%assign %%i 1 +%rep (in_channels - 1) + %if copy_src_from_stack + %define src_ptr src5q + mov src_ptr, src %+ %%i %+ m + %else + %define src_ptr src %+ %%i %+ q + %endif + ; avoid extra load for mono if matrix is in a mm register + %if stereo || mx_stack_0_ %+ %%i + mova m2, [src_ptr+lenq] + %endif + %if stereo + fmaddps m1, m2, mx_1_ %+ %%i, m1, m3 + %endif + %if stereo || mx_stack_0_ %+ %%i + fmaddps m0, m2, mx_0_ %+ %%i, m0, m2 + %else + fmaddps m0, mx_0_ %+ %%i, [src_ptr+lenq], m0, m1 + %endif + %assign %%i %%i+1 +%endrep + mova [src0q+lenq], m0 + %if stereo + mova [src1q+lenq], m1 + %endif +%endif + + add lenq, mmsize + jl .loop +; zero ymm high halves +%if mmsize == 32 + vzeroupper +%endif + RET +%endmacro + +%macro MIX_3_8_TO_1_2_FLT_FUNCS 0 +%assign %%i 3 +%rep 6 + INIT_XMM sse + MIX_3_8_TO_1_2_FLT %%i, 1, fltp + MIX_3_8_TO_1_2_FLT %%i, 2, fltp + INIT_XMM sse2 + MIX_3_8_TO_1_2_FLT %%i, 1, s16p + MIX_3_8_TO_1_2_FLT %%i, 2, s16p + INIT_XMM sse4 + MIX_3_8_TO_1_2_FLT %%i, 1, s16p + MIX_3_8_TO_1_2_FLT %%i, 2, s16p + ; do not use ymm AVX or FMA4 in x86-32 for 6 or more channels due to stack alignment issues + %if HAVE_AVX_EXTERNAL + %if ARCH_X86_64 || %%i < 6 + INIT_YMM avx + %else + INIT_XMM avx + %endif + MIX_3_8_TO_1_2_FLT %%i, 1, fltp + MIX_3_8_TO_1_2_FLT %%i, 2, fltp + INIT_XMM avx + MIX_3_8_TO_1_2_FLT %%i, 1, s16p + MIX_3_8_TO_1_2_FLT %%i, 2, s16p + %endif + %if HAVE_FMA4_EXTERNAL + %if ARCH_X86_64 || %%i < 6 + INIT_YMM fma4 + %else + INIT_XMM fma4 + %endif + MIX_3_8_TO_1_2_FLT %%i, 1, fltp + MIX_3_8_TO_1_2_FLT %%i, 2, fltp + INIT_XMM fma4 + MIX_3_8_TO_1_2_FLT %%i, 1, s16p + MIX_3_8_TO_1_2_FLT %%i, 2, s16p + %endif + %assign %%i %%i+1 +%endrep +%endmacro + +MIX_3_8_TO_1_2_FLT_FUNCS diff --git a/lib/ffmpeg/libavresample/x86/audio_mix_init.c b/lib/ffmpeg/libavresample/x86/audio_mix_init.c new file mode 100644 index 0000000000..72b2397ad6 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/audio_mix_init.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "libavutil/cpu.h" +#include "libavutil/x86/cpu.h" +#include "libavresample/audio_mix.h" + +extern void ff_mix_2_to_1_fltp_flt_sse(float **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_2_to_1_fltp_flt_avx(float **src, float **matrix, int len, + int out_ch, int in_ch); + +extern void ff_mix_2_to_1_s16p_flt_sse2(int16_t **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_2_to_1_s16p_flt_sse4(int16_t **src, float **matrix, int len, + int out_ch, int in_ch); + +extern void ff_mix_2_to_1_s16p_q8_sse2(int16_t **src, int16_t **matrix, + int len, int out_ch, int in_ch); + +extern void ff_mix_1_to_2_fltp_flt_sse(float **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_1_to_2_fltp_flt_avx(float **src, float **matrix, int len, + int out_ch, int in_ch); + +extern void ff_mix_1_to_2_s16p_flt_sse2(int16_t **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_1_to_2_s16p_flt_sse4(int16_t **src, float **matrix, int len, + int out_ch, int in_ch); +extern void ff_mix_1_to_2_s16p_flt_avx (int16_t **src, float **matrix, int len, + int out_ch, int in_ch); + +#define DEFINE_MIX_3_8_TO_1_2(chan) \ +extern void ff_mix_ ## chan ## _to_1_fltp_flt_sse(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_fltp_flt_sse(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_s16p_flt_sse2(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_s16p_flt_sse2(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_s16p_flt_sse4(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_s16p_flt_sse4(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_fltp_flt_avx(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_fltp_flt_avx(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_s16p_flt_avx(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_s16p_flt_avx(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_fltp_flt_fma4(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_fltp_flt_fma4(float **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ + \ +extern void ff_mix_ ## chan ## _to_1_s16p_flt_fma4(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); \ +extern void ff_mix_ ## chan ## _to_2_s16p_flt_fma4(int16_t **src, \ + float **matrix, int len, \ + int out_ch, int in_ch); + +DEFINE_MIX_3_8_TO_1_2(3) +DEFINE_MIX_3_8_TO_1_2(4) +DEFINE_MIX_3_8_TO_1_2(5) +DEFINE_MIX_3_8_TO_1_2(6) +DEFINE_MIX_3_8_TO_1_2(7) +DEFINE_MIX_3_8_TO_1_2(8) + +#define SET_MIX_3_8_TO_1_2(chan) \ + if (EXTERNAL_SSE(mm_flags)) { \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, 16, 4, "SSE", \ + ff_mix_ ## chan ## _to_1_fltp_flt_sse); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, 16, 4, "SSE", \ + ff_mix_## chan ##_to_2_fltp_flt_sse); \ + } \ + if (EXTERNAL_SSE2(mm_flags)) { \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, 16, 8, "SSE2", \ + ff_mix_ ## chan ## _to_1_s16p_flt_sse2); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, 16, 8, "SSE2", \ + ff_mix_ ## chan ## _to_2_s16p_flt_sse2); \ + } \ + if (EXTERNAL_SSE4(mm_flags)) { \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, 16, 8, "SSE4", \ + ff_mix_ ## chan ## _to_1_s16p_flt_sse4); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, 16, 8, "SSE4", \ + ff_mix_ ## chan ## _to_2_s16p_flt_sse4); \ + } \ + if (EXTERNAL_AVX(mm_flags)) { \ + int ptr_align = 32; \ + int smp_align = 8; \ + if (ARCH_X86_32 || chan >= 6) { \ + ptr_align = 16; \ + smp_align = 4; \ + } \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, ptr_align, smp_align, "AVX", \ + ff_mix_ ## chan ## _to_1_fltp_flt_avx); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, ptr_align, smp_align, "AVX", \ + ff_mix_ ## chan ## _to_2_fltp_flt_avx); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, 16, 8, "AVX", \ + ff_mix_ ## chan ## _to_1_s16p_flt_avx); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, 16, 8, "AVX", \ + ff_mix_ ## chan ## _to_2_s16p_flt_avx); \ + } \ + if (EXTERNAL_FMA4(mm_flags)) { \ + int ptr_align = 32; \ + int smp_align = 8; \ + if (ARCH_X86_32 || chan >= 6) { \ + ptr_align = 16; \ + smp_align = 4; \ + } \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, ptr_align, smp_align, "FMA4", \ + ff_mix_ ## chan ## _to_1_fltp_flt_fma4); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, ptr_align, smp_align, "FMA4", \ + ff_mix_ ## chan ## _to_2_fltp_flt_fma4); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 1, 16, 8, "FMA4", \ + ff_mix_ ## chan ## _to_1_s16p_flt_fma4); \ + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT,\ + chan, 2, 16, 8, "FMA4", \ + ff_mix_ ## chan ## _to_2_s16p_flt_fma4); \ + } + +av_cold void ff_audio_mix_init_x86(AudioMix *am) +{ +#if HAVE_YASM + int mm_flags = av_get_cpu_flags(); + + if (EXTERNAL_SSE(mm_flags)) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 16, 8, "SSE", ff_mix_2_to_1_fltp_flt_sse); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 16, 4, "SSE", ff_mix_1_to_2_fltp_flt_sse); + } + if (EXTERNAL_SSE2(mm_flags)) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 16, 8, "SSE2", ff_mix_2_to_1_s16p_flt_sse2); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_Q8, + 2, 1, 16, 8, "SSE2", ff_mix_2_to_1_s16p_q8_sse2); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 16, 8, "SSE2", ff_mix_1_to_2_s16p_flt_sse2); + } + if (EXTERNAL_SSE4(mm_flags)) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 16, 8, "SSE4", ff_mix_2_to_1_s16p_flt_sse4); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 16, 8, "SSE4", ff_mix_1_to_2_s16p_flt_sse4); + } + if (EXTERNAL_AVX(mm_flags)) { + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 2, 1, 32, 16, "AVX", ff_mix_2_to_1_fltp_flt_avx); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_FLTP, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 32, 8, "AVX", ff_mix_1_to_2_fltp_flt_avx); + ff_audio_mix_set_func(am, AV_SAMPLE_FMT_S16P, AV_MIX_COEFF_TYPE_FLT, + 1, 2, 16, 8, "AVX", ff_mix_1_to_2_s16p_flt_avx); + } + + SET_MIX_3_8_TO_1_2(3) + SET_MIX_3_8_TO_1_2(4) + SET_MIX_3_8_TO_1_2(5) + SET_MIX_3_8_TO_1_2(6) + SET_MIX_3_8_TO_1_2(7) + SET_MIX_3_8_TO_1_2(8) +#endif /* HAVE_YASM */ +} diff --git a/lib/ffmpeg/libavresample/x86/dither.asm b/lib/ffmpeg/libavresample/x86/dither.asm new file mode 100644 index 0000000000..757f2800bc --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/dither.asm @@ -0,0 +1,117 @@ +;****************************************************************************** +;* x86 optimized dithering format conversion +;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> +;* +;* 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/x86util.asm" + +SECTION_RODATA 32 + +; 1.0f / (2.0f * INT32_MAX) +pf_dither_scale: times 8 dd 2.32830643762e-10 + +pf_s16_scale: times 4 dd 32753.0 + +SECTION_TEXT + +;------------------------------------------------------------------------------ +; void ff_quantize(int16_t *dst, float *src, float *dither, int len); +;------------------------------------------------------------------------------ + +INIT_XMM sse2 +cglobal quantize, 4,4,3, dst, src, dither, len + lea lenq, [2*lend] + add dstq, lenq + lea srcq, [srcq+2*lenq] + lea ditherq, [ditherq+2*lenq] + neg lenq + mova m2, [pf_s16_scale] +.loop: + mulps m0, m2, [srcq+2*lenq] + mulps m1, m2, [srcq+2*lenq+mmsize] + addps m0, [ditherq+2*lenq] + addps m1, [ditherq+2*lenq+mmsize] + cvtps2dq m0, m0 + cvtps2dq m1, m1 + packssdw m0, m1 + mova [dstq+lenq], m0 + add lenq, mmsize + jl .loop + REP_RET + +;------------------------------------------------------------------------------ +; void ff_dither_int_to_float_rectangular(float *dst, int *src, int len) +;------------------------------------------------------------------------------ + +%macro DITHER_INT_TO_FLOAT_RECTANGULAR 0 +cglobal dither_int_to_float_rectangular, 3,3,3, dst, src, len + lea lenq, [4*lend] + add srcq, lenq + add dstq, lenq + neg lenq + mova m0, [pf_dither_scale] +.loop: + cvtdq2ps m1, [srcq+lenq] + cvtdq2ps m2, [srcq+lenq+mmsize] + mulps m1, m1, m0 + mulps m2, m2, m0 + mova [dstq+lenq], m1 + mova [dstq+lenq+mmsize], m2 + add lenq, 2*mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +DITHER_INT_TO_FLOAT_RECTANGULAR +INIT_YMM avx +DITHER_INT_TO_FLOAT_RECTANGULAR + +;------------------------------------------------------------------------------ +; void ff_dither_int_to_float_triangular(float *dst, int *src0, int len) +;------------------------------------------------------------------------------ + +%macro DITHER_INT_TO_FLOAT_TRIANGULAR 0 +cglobal dither_int_to_float_triangular, 3,4,5, dst, src0, len, src1 + lea lenq, [4*lend] + lea src1q, [src0q+2*lenq] + add src0q, lenq + add dstq, lenq + neg lenq + mova m0, [pf_dither_scale] +.loop: + cvtdq2ps m1, [src0q+lenq] + cvtdq2ps m2, [src0q+lenq+mmsize] + cvtdq2ps m3, [src1q+lenq] + cvtdq2ps m4, [src1q+lenq+mmsize] + addps m1, m1, m3 + addps m2, m2, m4 + mulps m1, m1, m0 + mulps m2, m2, m0 + mova [dstq+lenq], m1 + mova [dstq+lenq+mmsize], m2 + add lenq, 2*mmsize + jl .loop + REP_RET +%endmacro + +INIT_XMM sse2 +DITHER_INT_TO_FLOAT_TRIANGULAR +INIT_YMM avx +DITHER_INT_TO_FLOAT_TRIANGULAR diff --git a/lib/ffmpeg/libavresample/x86/dither_init.c b/lib/ffmpeg/libavresample/x86/dither_init.c new file mode 100644 index 0000000000..6532887c09 --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/dither_init.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> + * + * 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 "config.h" +#include "libavutil/cpu.h" +#include "libavutil/x86/cpu.h" +#include "libavresample/dither.h" + +extern void ff_quantize_sse2(int16_t *dst, const float *src, float *dither, + int len); + +extern void ff_dither_int_to_float_rectangular_sse2(float *dst, int *src, int len); +extern void ff_dither_int_to_float_rectangular_avx(float *dst, int *src, int len); + +extern void ff_dither_int_to_float_triangular_sse2(float *dst, int *src0, int len); +extern void ff_dither_int_to_float_triangular_avx(float *dst, int *src0, int len); + +av_cold void ff_dither_init_x86(DitherDSPContext *ddsp, + enum AVResampleDitherMethod method) +{ + int mm_flags = av_get_cpu_flags(); + + if (EXTERNAL_SSE2(mm_flags)) { + ddsp->quantize = ff_quantize_sse2; + ddsp->ptr_align = 16; + ddsp->samples_align = 8; + } + + if (method == AV_RESAMPLE_DITHER_RECTANGULAR) { + if (EXTERNAL_SSE2(mm_flags)) { + ddsp->dither_int_to_float = ff_dither_int_to_float_rectangular_sse2; + } + if (EXTERNAL_AVX(mm_flags)) { + ddsp->dither_int_to_float = ff_dither_int_to_float_rectangular_avx; + } + } else { + if (EXTERNAL_SSE2(mm_flags)) { + ddsp->dither_int_to_float = ff_dither_int_to_float_triangular_sse2; + } + if (EXTERNAL_AVX(mm_flags)) { + ddsp->dither_int_to_float = ff_dither_int_to_float_triangular_avx; + } + } +} diff --git a/lib/ffmpeg/libavresample/x86/util.asm b/lib/ffmpeg/libavresample/x86/util.asm new file mode 100644 index 0000000000..0ce953159c --- /dev/null +++ b/lib/ffmpeg/libavresample/x86/util.asm @@ -0,0 +1,41 @@ +;****************************************************************************** +;* x86 utility macros for libavresample +;* Copyright (c) 2012 Justin Ruggles <justin.ruggles@gmail.com> +;* +;* This file is part of Libav. +;* +;* Libav is free software; you can redistribute it and/or +;* modify it under the terms of the GNU Lesser General Public +;* License as published by the Free Software Foundation; either +;* version 2.1 of the License, or (at your option) any later version. +;* +;* Libav is distributed in the hope that it will be useful, +;* but WITHOUT ANY WARRANTY; without even the implied warranty of +;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;* Lesser General Public License for more details. +;* +;* You should have received a copy of the GNU Lesser General Public +;* License along with Libav; if not, write to the Free Software +;* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;****************************************************************************** + +%macro S16_TO_S32_SX 2 ; src/low dst, high dst +%if cpuflag(sse4) + pmovsxwd m%2, m%1 + psrldq m%1, 8 + pmovsxwd m%1, m%1 + SWAP %1, %2 +%else + mova m%2, m%1 + punpckhwd m%2, m%2 + punpcklwd m%1, m%1 + psrad m%2, 16 + psrad m%1, 16 +%endif +%endmacro + +%macro DEINT2_PS 3 ; src0/even dst, src1/odd dst, temp + shufps m%3, m%1, m%2, q3131 + shufps m%1, m%2, q2020 + SWAP %2,%3 +%endmacro |