diff --git a/mythtv/libs/libmyth/audio/audioconvert.cpp b/mythtv/libs/libmyth/audio/audioconvert.cpp new file mode 100644 index 00000000000..4ece3e607f9 --- /dev/null +++ b/mythtv/libs/libmyth/audio/audioconvert.cpp @@ -0,0 +1,835 @@ + +/* + * Class AudioConvert + * Created by Jean-Yves Avenard on 10/06/13. + * + * Copyright (C) Bubblestuff Pty Ltd 2013 + * Copyright (C) foobum@gmail.com 2010 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include "mythconfig.h" +#include "mythlogging.h" +#include "audioconvert.h" + +extern "C" { +#include "libavcodec/avcodec.h" +#include "libswresample/swresample.h" +} + +#define LOC QString("AudioConvert: ") + +#define ISALIGN(x) (((unsigned long)x & 0xf) == 0) + +#if ARCH_X86 +static int has_sse2 = -1; + +// Check cpuid for SSE2 support on x86 / x86_64 +static inline bool sse_check() +{ + if (has_sse2 != -1) + return (bool)has_sse2; + __asm__( + // -fPIC - we may not clobber ebx/rbx +#if ARCH_X86_64 + "push %%rbx \n\t" +#else + "push %%ebx \n\t" +#endif + "mov $1, %%eax \n\t" + "cpuid \n\t" + "and $0x4000000, %%edx \n\t" + "shr $26, %%edx \n\t" +#if ARCH_X86_64 + "pop %%rbx \n\t" +#else + "pop %%ebx \n\t" +#endif + :"=d"(has_sse2) + ::"%eax","%ecx" + ); + return (bool)has_sse2; +} +#endif //ARCH_x86 + +#if !HAVE_LRINTF +static av_always_inline av_const long int lrintf(float x) +{ + return (int)(rint(x)); +} +#endif /* HAVE_LRINTF */ + +static inline float clipcheck(float f) +{ + if (f > 1.0f) f = 1.0f; + else if (f < -1.0f) f = -1.0f; + return f; +} + +/* + All toFloat variants require 16 byte aligned input and output buffers on x86 for SSE optimised operation + The SSE code processes 16 bytes at a time and leaves any remainder for the C + - there is no remainder in practice */ + +static int toFloat8(float* out, const uchar* in, int len) +{ + int i = 0; + float f = 1.0f / ((1<<7)); + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + i = loops << 4; + int a = 0x80808080; + + __asm__ volatile ( + "movd %3, %%xmm0 \n\t" + "movd %4, %%xmm7 \n\t" + "punpckldq %%xmm0, %%xmm0 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm0, %%xmm0 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "movdqa (%1), %%xmm1 \n\t" + "xorpd %%xmm2, %%xmm2 \n\t" + "xorpd %%xmm3, %%xmm3 \n\t" + "psubb %%xmm0, %%xmm1 \n\t" + "xorpd %%xmm4, %%xmm4 \n\t" + "punpcklbw %%xmm1, %%xmm2 \n\t" + "xorpd %%xmm5, %%xmm5 \n\t" + "punpckhbw %%xmm1, %%xmm3 \n\t" + "punpcklwd %%xmm2, %%xmm4 \n\t" + "xorpd %%xmm6, %%xmm6 \n\t" + "punpckhwd %%xmm2, %%xmm5 \n\t" + "psrad $24, %%xmm4 \n\t" + "punpcklwd %%xmm3, %%xmm6 \n\t" + "psrad $24, %%xmm5 \n\t" + "punpckhwd %%xmm3, %%xmm1 \n\t" + "psrad $24, %%xmm6 \n\t" + "cvtdq2ps %%xmm4, %%xmm4 \n\t" + "psrad $24, %%xmm1 \n\t" + "cvtdq2ps %%xmm5, %%xmm5 \n\t" + "mulps %%xmm7, %%xmm4 \n\t" + "cvtdq2ps %%xmm6, %%xmm6 \n\t" + "mulps %%xmm7, %%xmm5 \n\t" + "movaps %%xmm4, (%0) \n\t" + "cvtdq2ps %%xmm1, %%xmm1 \n\t" + "mulps %%xmm7, %%xmm6 \n\t" + "movaps %%xmm5, 16(%0) \n\t" + "mulps %%xmm7, %%xmm1 \n\t" + "movaps %%xmm6, 32(%0) \n\t" + "add $16, %1 \n\t" + "movaps %%xmm1, 48(%0) \n\t" + "add $64, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out),"+r"(in) + :"c"(loops), "r"(a), "r"(f) + ); + } +#endif //ARCH_x86 + for (; i < len; i++) + *out++ = (*in++ - 0x80) * f; + return len << 2; +} + +/* + The SSE code processes 16 bytes at a time and leaves any remainder for the C + - there is no remainder in practice */ + +static inline uchar clip_uchar(int a) +{ + if (a&(~0xFF)) return (-a)>>31; + else return a; +} + +static int fromFloat8(uchar* out, const float* in, int len) +{ + int i = 0; + float f = (1<<7); + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + i = loops << 4; + int a = 0x80808080; + + __asm__ volatile ( + "movd %3, %%xmm0 \n\t" + "movd %4, %%xmm7 \n\t" + "punpckldq %%xmm0, %%xmm0 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm0, %%xmm0 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "movups (%1), %%xmm1 \n\t" + "movups 16(%1), %%xmm2 \n\t" + "mulps %%xmm7, %%xmm1 \n\t" + "movups 32(%1), %%xmm3 \n\t" + "mulps %%xmm7, %%xmm2 \n\t" + "cvtps2dq %%xmm1, %%xmm1 \n\t" + "movups 48(%1), %%xmm4 \n\t" + "mulps %%xmm7, %%xmm3 \n\t" + "cvtps2dq %%xmm2, %%xmm2 \n\t" + "mulps %%xmm7, %%xmm4 \n\t" + "cvtps2dq %%xmm3, %%xmm3 \n\t" + "packssdw %%xmm2, %%xmm1 \n\t" + "cvtps2dq %%xmm4, %%xmm4 \n\t" + "packssdw %%xmm4, %%xmm3 \n\t" + "add $64, %1 \n\t" + "packsswb %%xmm3, %%xmm1 \n\t" + "paddb %%xmm0, %%xmm1 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "add $16, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out),"+r"(in) + :"c"(loops), "r"(a), "r"(f) + ); + } +#endif //ARCH_x86 + for (;i < len; i++) + *out++ = clip_uchar(lrintf(*in++ * f) + 0x80); + return len; +} + +static int toFloat16(float* out, const short* in, int len) +{ + int i = 0; + float f = 1.0f / ((1<<15)); + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + i = loops << 4; + + __asm__ volatile ( + "movd %3, %%xmm7 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "xorpd %%xmm2, %%xmm2 \n\t" + "movdqa (%1), %%xmm1 \n\t" + "xorpd %%xmm3, %%xmm3 \n\t" + "punpcklwd %%xmm1, %%xmm2 \n\t" + "movdqa 16(%1), %%xmm4 \n\t" + "punpckhwd %%xmm1, %%xmm3 \n\t" + "psrad $16, %%xmm2 \n\t" + "punpcklwd %%xmm4, %%xmm5 \n\t" + "psrad $16, %%xmm3 \n\t" + "cvtdq2ps %%xmm2, %%xmm2 \n\t" + "punpckhwd %%xmm4, %%xmm6 \n\t" + "psrad $16, %%xmm5 \n\t" + "mulps %%xmm7, %%xmm2 \n\t" + "cvtdq2ps %%xmm3, %%xmm3 \n\t" + "psrad $16, %%xmm6 \n\t" + "mulps %%xmm7, %%xmm3 \n\t" + "cvtdq2ps %%xmm5, %%xmm5 \n\t" + "movaps %%xmm2, (%0) \n\t" + "cvtdq2ps %%xmm6, %%xmm6 \n\t" + "mulps %%xmm7, %%xmm5 \n\t" + "movaps %%xmm3, 16(%0) \n\t" + "mulps %%xmm7, %%xmm6 \n\t" + "movaps %%xmm5, 32(%0) \n\t" + "add $32, %1 \n\t" + "movaps %%xmm6, 48(%0) \n\t" + "add $64, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out),"+r"(in) + :"c"(loops), "r"(f) + ); + } +#endif //ARCH_x86 + for (; i < len; i++) + *out++ = *in++ * f; + return len << 2; +} + +static inline short clip_short(int a) +{ + if ((a+0x8000) & ~0xFFFF) return (a>>31) ^ 0x7FFF; + else return a; +} + +static int fromFloat16(short* out, const float* in, int len) +{ + int i = 0; + float f = (1<<15); + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + i = loops << 4; + + __asm__ volatile ( + "movd %3, %%xmm7 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "movups (%1), %%xmm1 \n\t" + "movups 16(%1), %%xmm2 \n\t" + "mulps %%xmm7, %%xmm1 \n\t" + "movups 32(%1), %%xmm3 \n\t" + "mulps %%xmm7, %%xmm2 \n\t" + "cvtps2dq %%xmm1, %%xmm1 \n\t" + "movups 48(%1), %%xmm4 \n\t" + "mulps %%xmm7, %%xmm3 \n\t" + "cvtps2dq %%xmm2, %%xmm2 \n\t" + "mulps %%xmm7, %%xmm4 \n\t" + "cvtps2dq %%xmm3, %%xmm3 \n\t" + "cvtps2dq %%xmm4, %%xmm4 \n\t" + "packssdw %%xmm2, %%xmm1 \n\t" + "packssdw %%xmm4, %%xmm3 \n\t" + "add $64, %1 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "movdqu %%xmm3, 16(%0) \n\t" + "add $32, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out),"+r"(in) + :"c"(loops), "r"(f) + ); + } +#endif //ARCH_x86 + for (;i < len;i++) + *out++ = clip_short(lrintf(*in++ * f)); + return len << 1; +} + +static int toFloat32(AudioFormat format, float* out, const int* in, int len) +{ + int i = 0; + int bits = AudioOutputSettings::FormatToBits(format); + float f = 1.0f / ((uint)(1<<(bits-1))); + int shift = 32 - bits; + + if (format == FORMAT_S24LSB) + shift = 0; + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + i = loops << 4; + + __asm__ volatile ( + "movd %3, %%xmm7 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "movd %4, %%xmm6 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "movdqa (%1), %%xmm1 \n\t" + "movdqa 16(%1), %%xmm2 \n\t" + "psrad %%xmm6, %%xmm1 \n\t" + "movdqa 32(%1), %%xmm3 \n\t" + "cvtdq2ps %%xmm1, %%xmm1 \n\t" + "psrad %%xmm6, %%xmm2 \n\t" + "movdqa 48(%1), %%xmm4 \n\t" + "cvtdq2ps %%xmm2, %%xmm2 \n\t" + "psrad %%xmm6, %%xmm3 \n\t" + "mulps %%xmm7, %%xmm1 \n\t" + "psrad %%xmm6, %%xmm4 \n\t" + "cvtdq2ps %%xmm3, %%xmm3 \n\t" + "movaps %%xmm1, (%0) \n\t" + "mulps %%xmm7, %%xmm2 \n\t" + "cvtdq2ps %%xmm4, %%xmm4 \n\t" + "movaps %%xmm2, 16(%0) \n\t" + "mulps %%xmm7, %%xmm3 \n\t" + "mulps %%xmm7, %%xmm4 \n\t" + "movaps %%xmm3, 32(%0) \n\t" + "add $64, %1 \n\t" + "movaps %%xmm4, 48(%0) \n\t" + "add $64, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out),"+r"(in) + :"c"(loops), "r"(f), "r"(shift) + ); + } +#endif //ARCH_x86 + for (; i < len; i++) + *out++ = (*in++ >> shift) * f; + return len << 2; +} + +static int fromFloat32(AudioFormat format, int* out, const float* in, int len) +{ + int i = 0; + int bits = AudioOutputSettings::FormatToBits(format); + float f = (uint)(1<<(bits-1)); + int shift = 32 - bits; + + if (format == FORMAT_S24LSB) + shift = 0; + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + float o = 1, mo = -1; + int loops = len >> 4; + i = loops << 4; + + __asm__ volatile ( + "movd %3, %%xmm7 \n\t" + "movss %4, %%xmm5 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "movss %5, %%xmm6 \n\t" + "punpckldq %%xmm5, %%xmm5 \n\t" + "punpckldq %%xmm6, %%xmm6 \n\t" + "movd %6, %%xmm0 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm5, %%xmm5 \n\t" + "punpckldq %%xmm6, %%xmm6 \n\t" + "1: \n\t" + "movups (%1), %%xmm1 \n\t" + "movups 16(%1), %%xmm2 \n\t" + "minps %%xmm5, %%xmm1 \n\t" + "movups 32(%1), %%xmm3 \n\t" + "maxps %%xmm6, %%xmm1 \n\t" + "movups 48(%1), %%xmm4 \n\t" + "mulps %%xmm7, %%xmm1 \n\t" + "minps %%xmm5, %%xmm2 \n\t" + "cvtps2dq %%xmm1, %%xmm1 \n\t" + "maxps %%xmm6, %%xmm2 \n\t" + "pslld %%xmm0, %%xmm1 \n\t" + "minps %%xmm5, %%xmm3 \n\t" + "mulps %%xmm7, %%xmm2 \n\t" + "movdqu %%xmm1, (%0) \n\t" + "cvtps2dq %%xmm2, %%xmm2 \n\t" + "maxps %%xmm6, %%xmm3 \n\t" + "minps %%xmm5, %%xmm4 \n\t" + "pslld %%xmm0, %%xmm2 \n\t" + "mulps %%xmm7, %%xmm3 \n\t" + "maxps %%xmm6, %%xmm4 \n\t" + "movdqu %%xmm2, 16(%0) \n\t" + "cvtps2dq %%xmm3, %%xmm3 \n\t" + "mulps %%xmm7, %%xmm4 \n\t" + "pslld %%xmm0, %%xmm3 \n\t" + "cvtps2dq %%xmm4, %%xmm4 \n\t" + "movdqu %%xmm3, 32(%0) \n\t" + "pslld %%xmm0, %%xmm4 \n\t" + "add $64, %1 \n\t" + "movdqu %%xmm4, 48(%0) \n\t" + "add $64, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out), "+r"(in) + :"c"(loops), "r"(f), "m"(o), "m"(mo), "r"(shift) + ); + } +#endif //ARCH_x86 + for (;i < len;i++) + *out++ = lrintf(clipcheck(*in++) * f) << shift; + return len << 2; +} + +static int fromFloatFLT(float* out, const float* in, int len) +{ + int i = 0; + +#if ARCH_X86 + if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) + { + int loops = len >> 4; + float o = 1, mo = -1; + i = loops << 4; + + __asm__ volatile ( + "movss %3, %%xmm6 \n\t" + "movss %4, %%xmm7 \n\t" + "punpckldq %%xmm6, %%xmm6 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "punpckldq %%xmm6, %%xmm6 \n\t" + "punpckldq %%xmm7, %%xmm7 \n\t" + "1: \n\t" + "movups (%1), %%xmm1 \n\t" + "movups 16(%1), %%xmm2 \n\t" + "minps %%xmm6, %%xmm1 \n\t" + "movups 32(%1), %%xmm3 \n\t" + "maxps %%xmm7, %%xmm1 \n\t" + "minps %%xmm6, %%xmm2 \n\t" + "movups 48(%1), %%xmm4 \n\t" + "maxps %%xmm7, %%xmm2 \n\t" + "movups %%xmm1, (%0) \n\t" + "minps %%xmm6, %%xmm3 \n\t" + "movups %%xmm2, 16(%0) \n\t" + "maxps %%xmm7, %%xmm3 \n\t" + "minps %%xmm6, %%xmm4 \n\t" + "movups %%xmm3, 32(%0) \n\t" + "maxps %%xmm7, %%xmm4 \n\t" + "add $64, %1 \n\t" + "movups %%xmm4, 48(%0) \n\t" + "add $64, %0 \n\t" + "sub $1, %%ecx \n\t" + "jnz 1b \n\t" + :"+r"(out), "+r"(in) + :"c"(loops), "m"(o), "m"(mo) + ); + } +#endif //ARCH_x86 + for (;i < len;i++) + *out++ = clipcheck(*in++); + return len << 2; +} + +/** + * Convert integer samples to floats + * + * Consumes 'bytes' bytes from in and returns the numer of bytes written to out + */ +int AudioConvert::toFloat(AudioFormat format, void* out, const void* in, + int bytes) +{ + if (bytes <= 0) + return 0; + + switch (format) + { + case FORMAT_U8: + return toFloat8((float*)out, (uchar*)in, bytes); + case FORMAT_S16: + return toFloat16((float*)out, (short*)in, bytes >> 1); + case FORMAT_S24: + case FORMAT_S24LSB: + case FORMAT_S32: + return toFloat32(format, (float*)out, (int*)in, bytes >> 2); + case FORMAT_FLT: + memcpy(out, in, bytes); + return bytes; + case FORMAT_NONE: + default: + return 0; + } +} + +/** + * Convert float samples to integers + * + * Consumes 'bytes' bytes from in and returns the numer of bytes written to out + */ +int AudioConvert::fromFloat(AudioFormat format, void* out, const void* in, + int bytes) +{ + if (bytes <= 0) + return 0; + + switch (format) + { + case FORMAT_U8: + return fromFloat8((uchar*)out, (float*)in, bytes >> 2); + case FORMAT_S16: + return fromFloat16((short*)out, (float*)in, bytes >> 2); + case FORMAT_S24: + case FORMAT_S24LSB: + case FORMAT_S32: + return fromFloat32(format, (int*)out, (float*)in, bytes >> 2); + case FORMAT_FLT: + return fromFloatFLT((float*)out, (float*)in, bytes >> 2); + case FORMAT_NONE: + default: + return 0; + } +} + +/////// AudioConvert Class + +class AudioConvertInternal +{ +public: + AudioConvertInternal(AVSampleFormat in, AVSampleFormat out) : + m_in(in), m_out(out) + { + char error[AV_ERROR_MAX_STRING_SIZE]; + m_swr = swr_alloc_set_opts(NULL, + av_get_default_channel_layout(1), + m_out, + 48000, + av_get_default_channel_layout(1), + m_in, + 48000, + 0, NULL); + if (!m_swr) + { + LOG(VB_AUDIO, LOG_ERR, LOC + "error allocating resampler context"); + return; + } + /* initialize the resampling context */ + int ret = swr_init(m_swr); + if (ret < 0) + { + LOG(VB_AUDIO, LOG_ERR, LOC + + QString("error initializing resampler context (%1)") + .arg(av_make_error_string(error, sizeof(error), ret))); + swr_free(&m_swr); + return; + } + } + int Process(void* out, const void* in, int bytes) + { + if (!m_swr) + return -1; + + uint8_t* outp[] = {(uint8_t*)out}; + const uint8_t* inp[] = {(const uint8_t*)in}; + int samples = bytes / av_get_bytes_per_sample(m_in); + int ret = swr_convert(m_swr, + outp, samples, + inp, samples); + if (ret < 0) + return ret; + return ret * av_get_bytes_per_sample(m_out); + } + ~AudioConvertInternal() + { + if (m_swr) + { + swr_free(&m_swr); + } + } + + SwrContext* m_swr; + AVSampleFormat m_in, m_out; +}; + + +AudioConvert::AudioConvert(AudioFormat in, AudioFormat out) : + m_ctx(NULL), m_in(in), m_out(out) +{ +} + +AudioConvert::~AudioConvert() +{ + delete m_ctx; + m_ctx = NULL; +} + +/** + * Convert samples from one format to another + * + * Consumes 'bytes' bytes from in and returns the numer of bytes written to out + * return negative number if error + */ +int AudioConvert::Process(void* out, const void* in, int bytes) +{ + if (bytes <= 0) + return 0; + if (m_out == FORMAT_NONE || m_in == FORMAT_NONE) + return 0; + + /* use conversion routines to perform clipping on samples */ + if (m_in == FORMAT_FLT) + return fromFloat(m_out, out, in, bytes); + if (m_out == FORMAT_FLT) + return toFloat(m_in, out, in, bytes); + + if (m_in == m_out) + { + memcpy(out, in, bytes); + return bytes; + } + + if (m_in == FORMAT_S24 || m_in == FORMAT_S24LSB || + m_out == FORMAT_S24 || m_out == FORMAT_S24LSB) + { + // FFmpeg can't handle those, so use float conversion intermediary + if (AudioOutputSettings::SampleSize(m_out) == AudioOutputSettings::SampleSize(FORMAT_FLT)) + { + // this can be done in place + int s = toFloat(m_in, out, in, bytes); + return fromFloat(m_out, out, out, s); + } + // this leave S24 -> U8/S16. + // TODO: native handling of those ; use internal temp buffer in the mean time + + uint8_t buffer[65536+15]; + uint8_t* tmp = (uint8_t*)(((long)buffer + 15) & ~0xf); + int left = bytes; + + while (left > 0) + { + int s; + + if (left >= 65536) + { + s = toFloat(m_in, tmp, in, 65536); + in = (void*)((long)in + s); + out = (void*)((long)out + fromFloat(m_out, out, tmp, s)); + left -= 65536; + continue; + } + s = toFloat(m_in, tmp, in, left); + in = (void*)((long)in + s); + out = (void*)((long)out + fromFloat(m_out, out, tmp, s)); + left = 0; + } + return bytes * AudioOutputSettings::SampleSize(m_out) / AudioOutputSettings::SampleSize(m_in); + } + + // use FFmpeg conversion routine for S32<->S16, S32<->U8 and S16<->U8 + if (!m_ctx) + { + m_ctx = new AudioConvertInternal(AudioOutputSettings::FormatToAVSampleFormat(m_in), + AudioOutputSettings::FormatToAVSampleFormat(m_out)); + } + + return m_ctx->Process(out, in, bytes); +} + +/** + * Convert a mono stream to stereo by copying and interleaving samples + */ +void AudioConvert::MonoToStereo(void* dst, const void* src, int samples) +{ + float* d = (float*)dst; + float* s = (float*)src; + for (int i = 0; i < samples; i++) + { + *d++ = *s; + *d++ = *s++; + } +} + +template +void _DeinterleaveSample(AudioDataType* out, const AudioDataType* in, int channels, int frames) +{ + AudioDataType* outp[8]; + + for (int i = 0; i < channels; i++) + { + outp[i] = out + (i * frames); + } + + for (int i = 0; i < frames; i++) + { + for (int j = 0; j < channels; j++) + { + *(outp[j]++) = *(in++); + } + } +} + +/** + * Deinterleave input samples + * Deinterleave audio samples and compact them + */ +void AudioConvert::DeinterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* input, + int data_size) +{ + int bits = AudioOutputSettings::FormatToBits(format); + if (bits == 8) + { + _DeinterleaveSample((char*)output, (const char*)input, channels, data_size/sizeof(char)/channels); + } + else if (bits == 16) + { + _DeinterleaveSample((short*)output, (const short*)input, channels, data_size/sizeof(short)/channels); + } + else + { + _DeinterleaveSample((int*)output, (const int*)input, channels, data_size/sizeof(int)/channels); + } +} + +template +void _InterleaveSample(AudioDataType* out, const AudioDataType* in, int channels, int frames, + const AudioDataType* const* inp = NULL) +{ + const AudioDataType* my_inp[8]; + + if (!inp) + { + // We're given an array of int, calculate pointers to each row + for (int i = 0; i < channels; i++) + { + my_inp[i] = in + (i * frames); + } + } + else + { + for (int i = 0; i < channels; i++) + { + my_inp[i] = inp[i]; + } + } + + for (int i = 0; i < frames; i++) + { + for (int j = 0; j < channels; j++) + { + *(out++) = *(my_inp[j]++); + } + } +} + +/** + * Interleave input samples + * Planar audio is contained in array of pointers + * Interleave audio samples (convert from planar format) + */ +void AudioConvert::InterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* const* input, + int data_size) +{ + int bits = AudioOutputSettings::FormatToBits(format); + if (bits == 8) + { + _InterleaveSample((char*)output, (const char*)NULL, channels, data_size/sizeof(char)/channels, + (const char* const*)input); + } + else if (bits == 16) + { + _InterleaveSample((short*)output, (const short*)NULL, channels, data_size/sizeof(short)/channels, + (const short* const*)input); + } + else + { + _InterleaveSample((int*)output, (const int*)NULL, channels, data_size/sizeof(int)/channels, + (const int* const*)input); + } +} + +/** + * Interleave input samples + * Interleave audio samples (convert from planar format) + */ +void AudioConvert::InterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* input, + int data_size) +{ + int bits = AudioOutputSettings::FormatToBits(format); + if (bits == 8) + { + _InterleaveSample((char*)output, (const char*)input, channels, data_size/sizeof(char)/channels); + } + else if (bits == 16) + { + _InterleaveSample((short*)output, (const short*)input, channels, data_size/sizeof(short)/channels); + } + else + { + _InterleaveSample((int*)output, (const int*)input, channels, data_size/sizeof(int)/channels); + } +} diff --git a/mythtv/libs/libmyth/audio/audioconvert.h b/mythtv/libs/libmyth/audio/audioconvert.h new file mode 100644 index 00000000000..d63b985673d --- /dev/null +++ b/mythtv/libs/libmyth/audio/audioconvert.h @@ -0,0 +1,72 @@ + +/* + * Class AudioConvert + * Created by Jean-Yves Avenard on 10/06/13. + * + * Copyright (C) Bubblestuff Pty Ltd 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef __MythXCode__audioconvert__ +#define __MythXCode__audioconvert__ + +#include "mythexp.h" +#include "audiooutputsettings.h" + +class AudioConvertInternal; + +class MPUBLIC AudioConvert +{ +public: + + AudioConvert(AudioFormat in, AudioFormat out); + virtual ~AudioConvert(); + /** + * Process + * Parameters: + * out : destination buffer where converted samples will be copied + * in : source buffer + * bytes: size in bytes of source buffer + * + * Return Value: size in bytes of samples converted or <= 0 if error + */ + int Process(void* out, const void* in, int bytes); + + bool operator==(AudioConvert& rhs) const + { return m_in == rhs.m_in && m_out == rhs.m_out; } + bool operator!=(AudioConvert& rhs) const + { return m_in != m_out; } + + // static utilities + static int toFloat(AudioFormat format, void* out, const void* in, int bytes); + static int fromFloat(AudioFormat format, void* out, const void* in, int bytes); + static void MonoToStereo(void* dst, const void* src, int samples); + static void DeinterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* input, + int data_size); + static void InterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* const* input, + int data_size); + static void InterleaveSamples(AudioFormat format, int channels, + uint8_t* output, const uint8_t* input, + int data_size); +private: + AudioConvertInternal* m_ctx; + AudioFormat m_in, m_out; +}; + +#endif /* defined(__MythXCode__audioconvert__) */ diff --git a/mythtv/libs/libmyth/audio/audiooutputsettings.cpp b/mythtv/libs/libmyth/audio/audiooutputsettings.cpp index 1e1b6f84c29..dcc609c2c77 100644 --- a/mythtv/libs/libmyth/audio/audiooutputsettings.cpp +++ b/mythtv/libs/libmyth/audio/audiooutputsettings.cpp @@ -202,6 +202,9 @@ int AudioOutputSettings::SampleSize(AudioFormat format) } } +/** + * Return AVSampleFormat closest equivalent to AudioFormat + */ AudioFormat AudioOutputSettings::AVSampleFormatToFormat(AVSampleFormat format, int bits) { switch (av_get_packed_sample_fmt(format)) @@ -222,6 +225,25 @@ AudioFormat AudioOutputSettings::AVSampleFormatToFormat(AVSampleFormat format, i } } +/** + * Return AudioFormat closest equivalent to AVSampleFormat + * Note that FORMAT_S24LSB and FORMAT_S24 have no direct equivalent + * use S32 instead + */ +AVSampleFormat AudioOutputSettings::FormatToAVSampleFormat(AudioFormat format) +{ + switch (format) + { + case FORMAT_U8: return AV_SAMPLE_FMT_U8; + case FORMAT_S16: return AV_SAMPLE_FMT_S16; + case FORMAT_FLT: return AV_SAMPLE_FMT_FLT; + case FORMAT_S32: + case FORMAT_S24: + case FORMAT_S24LSB: return AV_SAMPLE_FMT_S32; + default: return AV_SAMPLE_FMT_NONE; + } +} + void AudioOutputSettings::AddSupportedChannels(int channels) { m_channels.push_back(channels); diff --git a/mythtv/libs/libmyth/audio/audiooutputsettings.h b/mythtv/libs/libmyth/audio/audiooutputsettings.h index b1db24e174f..3bfe1aa8a53 100644 --- a/mythtv/libs/libmyth/audio/audiooutputsettings.h +++ b/mythtv/libs/libmyth/audio/audiooutputsettings.h @@ -72,6 +72,7 @@ class MPUBLIC AudioOutputSettings static const char* FormatToString(AudioFormat format); static int SampleSize(AudioFormat format); static AudioFormat AVSampleFormatToFormat(AVSampleFormat format, int bits = 0); + static AVSampleFormat FormatToAVSampleFormat(AudioFormat format); void AddSupportedChannels(int channels); bool IsSupportedChannels(int channels); int BestSupportedChannels(); diff --git a/mythtv/libs/libmyth/audio/audiooutpututil.cpp b/mythtv/libs/libmyth/audio/audiooutpututil.cpp index 91805da23bd..e5bb325c26c 100644 --- a/mythtv/libs/libmyth/audio/audiooutpututil.cpp +++ b/mythtv/libs/libmyth/audio/audiooutpututil.cpp @@ -6,10 +6,10 @@ #include "mythconfig.h" #include "mythlogging.h" #include "audiooutpututil.h" +#include "audioconvert.h" extern "C" { #include "libavcodec/avcodec.h" -#include "libswresample/swresample.h" #include "pink.h" } @@ -48,431 +48,6 @@ static inline bool sse_check() } #endif //ARCH_x86 -#if !HAVE_LRINTF -static av_always_inline av_const long int lrintf(float x) -{ - return (int)(rint(x)); -} -#endif /* HAVE_LRINTF */ - -static inline float clipcheck(float f) -{ - if (f > 1.0f) f = 1.0f; - else if (f < -1.0f) f = -1.0f; - return f; -} - -/* - All toFloat variants require 16 byte aligned input and output buffers on x86 for SSE optimised operation - The SSE code processes 16 bytes at a time and leaves any remainder for the C - - there is no remainder in practice */ - -static int toFloat8(float *out, uchar *in, int len) -{ - int i = 0; - float f = 1.0f / ((1<<7)); - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - i = loops << 4; - int a = 0x80808080; - - __asm__ volatile ( - "movd %3, %%xmm0 \n\t" - "movd %4, %%xmm7 \n\t" - "punpckldq %%xmm0, %%xmm0 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm0, %%xmm0 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "movdqa (%1), %%xmm1 \n\t" - "xorpd %%xmm2, %%xmm2 \n\t" - "xorpd %%xmm3, %%xmm3 \n\t" - "psubb %%xmm0, %%xmm1 \n\t" - "xorpd %%xmm4, %%xmm4 \n\t" - "punpcklbw %%xmm1, %%xmm2 \n\t" - "xorpd %%xmm5, %%xmm5 \n\t" - "punpckhbw %%xmm1, %%xmm3 \n\t" - "punpcklwd %%xmm2, %%xmm4 \n\t" - "xorpd %%xmm6, %%xmm6 \n\t" - "punpckhwd %%xmm2, %%xmm5 \n\t" - "psrad $24, %%xmm4 \n\t" - "punpcklwd %%xmm3, %%xmm6 \n\t" - "psrad $24, %%xmm5 \n\t" - "punpckhwd %%xmm3, %%xmm1 \n\t" - "psrad $24, %%xmm6 \n\t" - "cvtdq2ps %%xmm4, %%xmm4 \n\t" - "psrad $24, %%xmm1 \n\t" - "cvtdq2ps %%xmm5, %%xmm5 \n\t" - "mulps %%xmm7, %%xmm4 \n\t" - "cvtdq2ps %%xmm6, %%xmm6 \n\t" - "mulps %%xmm7, %%xmm5 \n\t" - "movaps %%xmm4, (%0) \n\t" - "cvtdq2ps %%xmm1, %%xmm1 \n\t" - "mulps %%xmm7, %%xmm6 \n\t" - "movaps %%xmm5, 16(%0) \n\t" - "mulps %%xmm7, %%xmm1 \n\t" - "movaps %%xmm6, 32(%0) \n\t" - "add $16, %1 \n\t" - "movaps %%xmm1, 48(%0) \n\t" - "add $64, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out),"+r"(in) - :"c"(loops), "r"(a), "r"(f) - ); - } -#endif //ARCH_x86 - for (; i < len; i++) - *out++ = (*in++ - 0x80) * f; - return len << 2; -} - -/* - The SSE code processes 16 bytes at a time and leaves any remainder for the C - - there is no remainder in practice */ - -static inline uchar clip_uchar(int a) -{ - if (a&(~0xFF)) return (-a)>>31; - else return a; -} - -static int fromFloat8(uchar *out, float *in, int len) -{ - int i = 0; - float f = (1<<7); - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - i = loops << 4; - int a = 0x80808080; - - __asm__ volatile ( - "movd %3, %%xmm0 \n\t" - "movd %4, %%xmm7 \n\t" - "punpckldq %%xmm0, %%xmm0 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm0, %%xmm0 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "movups (%1), %%xmm1 \n\t" - "movups 16(%1), %%xmm2 \n\t" - "mulps %%xmm7, %%xmm1 \n\t" - "movups 32(%1), %%xmm3 \n\t" - "mulps %%xmm7, %%xmm2 \n\t" - "cvtps2dq %%xmm1, %%xmm1 \n\t" - "movups 48(%1), %%xmm4 \n\t" - "mulps %%xmm7, %%xmm3 \n\t" - "cvtps2dq %%xmm2, %%xmm2 \n\t" - "mulps %%xmm7, %%xmm4 \n\t" - "cvtps2dq %%xmm3, %%xmm3 \n\t" - "packssdw %%xmm2, %%xmm1 \n\t" - "cvtps2dq %%xmm4, %%xmm4 \n\t" - "packssdw %%xmm4, %%xmm3 \n\t" - "add $64, %1 \n\t" - "packsswb %%xmm3, %%xmm1 \n\t" - "paddb %%xmm0, %%xmm1 \n\t" - "movdqu %%xmm1, (%0) \n\t" - "add $16, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out),"+r"(in) - :"c"(loops), "r"(a), "r"(f) - ); - } -#endif //ARCH_x86 - for (;i < len; i++) - *out++ = clip_uchar(lrintf(*in++ * f) + 0x80); - return len; -} - -static int toFloat16(float *out, short *in, int len) -{ - int i = 0; - float f = 1.0f / ((1<<15)); - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - i = loops << 4; - - __asm__ volatile ( - "movd %3, %%xmm7 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "xorpd %%xmm2, %%xmm2 \n\t" - "movdqa (%1), %%xmm1 \n\t" - "xorpd %%xmm3, %%xmm3 \n\t" - "punpcklwd %%xmm1, %%xmm2 \n\t" - "movdqa 16(%1), %%xmm4 \n\t" - "punpckhwd %%xmm1, %%xmm3 \n\t" - "psrad $16, %%xmm2 \n\t" - "punpcklwd %%xmm4, %%xmm5 \n\t" - "psrad $16, %%xmm3 \n\t" - "cvtdq2ps %%xmm2, %%xmm2 \n\t" - "punpckhwd %%xmm4, %%xmm6 \n\t" - "psrad $16, %%xmm5 \n\t" - "mulps %%xmm7, %%xmm2 \n\t" - "cvtdq2ps %%xmm3, %%xmm3 \n\t" - "psrad $16, %%xmm6 \n\t" - "mulps %%xmm7, %%xmm3 \n\t" - "cvtdq2ps %%xmm5, %%xmm5 \n\t" - "movaps %%xmm2, (%0) \n\t" - "cvtdq2ps %%xmm6, %%xmm6 \n\t" - "mulps %%xmm7, %%xmm5 \n\t" - "movaps %%xmm3, 16(%0) \n\t" - "mulps %%xmm7, %%xmm6 \n\t" - "movaps %%xmm5, 32(%0) \n\t" - "add $32, %1 \n\t" - "movaps %%xmm6, 48(%0) \n\t" - "add $64, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out),"+r"(in) - :"c"(loops), "r"(f) - ); - } -#endif //ARCH_x86 - for (; i < len; i++) - *out++ = *in++ * f; - return len << 2; -} - -static inline short clip_short(int a) -{ - if ((a+0x8000) & ~0xFFFF) return (a>>31) ^ 0x7FFF; - else return a; -} - -static int fromFloat16(short *out, float *in, int len) -{ - int i = 0; - float f = (1<<15); - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - i = loops << 4; - - __asm__ volatile ( - "movd %3, %%xmm7 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "movups (%1), %%xmm1 \n\t" - "movups 16(%1), %%xmm2 \n\t" - "mulps %%xmm7, %%xmm1 \n\t" - "movups 32(%1), %%xmm3 \n\t" - "mulps %%xmm7, %%xmm2 \n\t" - "cvtps2dq %%xmm1, %%xmm1 \n\t" - "movups 48(%1), %%xmm4 \n\t" - "mulps %%xmm7, %%xmm3 \n\t" - "cvtps2dq %%xmm2, %%xmm2 \n\t" - "mulps %%xmm7, %%xmm4 \n\t" - "cvtps2dq %%xmm3, %%xmm3 \n\t" - "cvtps2dq %%xmm4, %%xmm4 \n\t" - "packssdw %%xmm2, %%xmm1 \n\t" - "packssdw %%xmm4, %%xmm3 \n\t" - "add $64, %1 \n\t" - "movdqu %%xmm1, (%0) \n\t" - "movdqu %%xmm3, 16(%0) \n\t" - "add $32, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out),"+r"(in) - :"c"(loops), "r"(f) - ); - } -#endif //ARCH_x86 - for (;i < len;i++) - *out++ = clip_short(lrintf(*in++ * f)); - return len << 1; -} - -static int toFloat32(AudioFormat format, float *out, int *in, int len) -{ - int i = 0; - int bits = AudioOutputSettings::FormatToBits(format); - float f = 1.0f / ((uint)(1<<(bits-1))); - int shift = 32 - bits; - - if (format == FORMAT_S24LSB) - shift = 0; - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - i = loops << 4; - - __asm__ volatile ( - "movd %3, %%xmm7 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "movd %4, %%xmm6 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "movdqa (%1), %%xmm1 \n\t" - "movdqa 16(%1), %%xmm2 \n\t" - "psrad %%xmm6, %%xmm1 \n\t" - "movdqa 32(%1), %%xmm3 \n\t" - "cvtdq2ps %%xmm1, %%xmm1 \n\t" - "psrad %%xmm6, %%xmm2 \n\t" - "movdqa 48(%1), %%xmm4 \n\t" - "cvtdq2ps %%xmm2, %%xmm2 \n\t" - "psrad %%xmm6, %%xmm3 \n\t" - "mulps %%xmm7, %%xmm1 \n\t" - "psrad %%xmm6, %%xmm4 \n\t" - "cvtdq2ps %%xmm3, %%xmm3 \n\t" - "movaps %%xmm1, (%0) \n\t" - "mulps %%xmm7, %%xmm2 \n\t" - "cvtdq2ps %%xmm4, %%xmm4 \n\t" - "movaps %%xmm2, 16(%0) \n\t" - "mulps %%xmm7, %%xmm3 \n\t" - "mulps %%xmm7, %%xmm4 \n\t" - "movaps %%xmm3, 32(%0) \n\t" - "add $64, %1 \n\t" - "movaps %%xmm4, 48(%0) \n\t" - "add $64, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out),"+r"(in) - :"c"(loops), "r"(f), "r"(shift) - ); - } -#endif //ARCH_x86 - for (; i < len; i++) - *out++ = (*in++ >> shift) * f; - return len << 2; -} - -static int fromFloat32(AudioFormat format, int *out, float *in, int len) -{ - int i = 0; - int bits = AudioOutputSettings::FormatToBits(format); - float f = (uint)(1<<(bits-1)); - int shift = 32 - bits; - - if (format == FORMAT_S24LSB) - shift = 0; - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - float o = 1, mo = -1; - int loops = len >> 4; - i = loops << 4; - - __asm__ volatile ( - "movd %3, %%xmm7 \n\t" - "movss %4, %%xmm5 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "movss %5, %%xmm6 \n\t" - "punpckldq %%xmm5, %%xmm5 \n\t" - "punpckldq %%xmm6, %%xmm6 \n\t" - "movd %6, %%xmm0 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm5, %%xmm5 \n\t" - "punpckldq %%xmm6, %%xmm6 \n\t" - "1: \n\t" - "movups (%1), %%xmm1 \n\t" - "movups 16(%1), %%xmm2 \n\t" - "minps %%xmm5, %%xmm1 \n\t" - "movups 32(%1), %%xmm3 \n\t" - "maxps %%xmm6, %%xmm1 \n\t" - "movups 48(%1), %%xmm4 \n\t" - "mulps %%xmm7, %%xmm1 \n\t" - "minps %%xmm5, %%xmm2 \n\t" - "cvtps2dq %%xmm1, %%xmm1 \n\t" - "maxps %%xmm6, %%xmm2 \n\t" - "pslld %%xmm0, %%xmm1 \n\t" - "minps %%xmm5, %%xmm3 \n\t" - "mulps %%xmm7, %%xmm2 \n\t" - "movdqu %%xmm1, (%0) \n\t" - "cvtps2dq %%xmm2, %%xmm2 \n\t" - "maxps %%xmm6, %%xmm3 \n\t" - "minps %%xmm5, %%xmm4 \n\t" - "pslld %%xmm0, %%xmm2 \n\t" - "mulps %%xmm7, %%xmm3 \n\t" - "maxps %%xmm6, %%xmm4 \n\t" - "movdqu %%xmm2, 16(%0) \n\t" - "cvtps2dq %%xmm3, %%xmm3 \n\t" - "mulps %%xmm7, %%xmm4 \n\t" - "pslld %%xmm0, %%xmm3 \n\t" - "cvtps2dq %%xmm4, %%xmm4 \n\t" - "movdqu %%xmm3, 32(%0) \n\t" - "pslld %%xmm0, %%xmm4 \n\t" - "add $64, %1 \n\t" - "movdqu %%xmm4, 48(%0) \n\t" - "add $64, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out), "+r"(in) - :"c"(loops), "r"(f), "m"(o), "m"(mo), "r"(shift) - ); - } -#endif //ARCH_x86 - for (;i < len;i++) - *out++ = lrintf(clipcheck(*in++) * f) << shift; - return len << 2; -} - -static int fromFloatFLT(float *out, float *in, int len) -{ - int i = 0; - -#if ARCH_X86 - if (sse_check() && len >= 16 && ISALIGN(in) && ISALIGN(out)) - { - int loops = len >> 4; - float o = 1, mo = -1; - i = loops << 4; - - __asm__ volatile ( - "movss %3, %%xmm6 \n\t" - "movss %4, %%xmm7 \n\t" - "punpckldq %%xmm6, %%xmm6 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "punpckldq %%xmm6, %%xmm6 \n\t" - "punpckldq %%xmm7, %%xmm7 \n\t" - "1: \n\t" - "movups (%1), %%xmm1 \n\t" - "movups 16(%1), %%xmm2 \n\t" - "minps %%xmm6, %%xmm1 \n\t" - "movups 32(%1), %%xmm3 \n\t" - "maxps %%xmm7, %%xmm1 \n\t" - "minps %%xmm6, %%xmm2 \n\t" - "movups 48(%1), %%xmm4 \n\t" - "maxps %%xmm7, %%xmm2 \n\t" - "movups %%xmm1, (%0) \n\t" - "minps %%xmm6, %%xmm3 \n\t" - "movups %%xmm2, 16(%0) \n\t" - "maxps %%xmm7, %%xmm3 \n\t" - "minps %%xmm6, %%xmm4 \n\t" - "movups %%xmm3, 32(%0) \n\t" - "maxps %%xmm7, %%xmm4 \n\t" - "add $64, %1 \n\t" - "movups %%xmm4, 48(%0) \n\t" - "add $64, %0 \n\t" - "sub $1, %%ecx \n\t" - "jnz 1b \n\t" - :"+r"(out), "+r"(in) - :"c"(loops), "m"(o), "m"(mo) - ); - } -#endif //ARCH_x86 - for (;i < len;i++) - *out++ = clipcheck(*in++); - return len << 2; -} - /** * Returns true if platform has an FPU. * for the time being, this test is limited to testing if SSE2 is supported @@ -491,29 +66,10 @@ bool AudioOutputUtil::has_hardware_fpu() * * Consumes 'bytes' bytes from in and returns the numer of bytes written to out */ -int AudioOutputUtil::toFloat(AudioFormat format, void *out, void *in, +int AudioOutputUtil::toFloat(AudioFormat format, void *out, const void *in, int bytes) { - if (bytes <= 0) - return 0; - - switch (format) - { - case FORMAT_U8: - return toFloat8((float *)out, (uchar *)in, bytes); - case FORMAT_S16: - return toFloat16((float *)out, (short *)in, bytes >> 1); - case FORMAT_S24: - case FORMAT_S24LSB: - case FORMAT_S32: - return toFloat32(format, (float *)out, (int *)in, bytes >> 2); - case FORMAT_FLT: - memcpy(out, in, bytes); - return bytes; - case FORMAT_NONE: - default: - return 0; - } + return AudioConvert::toFloat(format, out, in, bytes); } /** @@ -521,42 +77,18 @@ int AudioOutputUtil::toFloat(AudioFormat format, void *out, void *in, * * Consumes 'bytes' bytes from in and returns the numer of bytes written to out */ -int AudioOutputUtil::fromFloat(AudioFormat format, void *out, void *in, +int AudioOutputUtil::fromFloat(AudioFormat format, void *out, const void *in, int bytes) { - if (bytes <= 0) - return 0; - - switch (format) - { - case FORMAT_U8: - return fromFloat8((uchar *)out, (float *)in, bytes >> 2); - case FORMAT_S16: - return fromFloat16((short *)out, (float *)in, bytes >> 2); - case FORMAT_S24: - case FORMAT_S24LSB: - case FORMAT_S32: - return fromFloat32(format, (int *)out, (float *)in, bytes >> 2); - case FORMAT_FLT: - return fromFloatFLT((float *)out, (float *)in, bytes >> 2); - case FORMAT_NONE: - default: - return 0; - } + return AudioConvert::fromFloat(format, out, in, bytes); } /** * Convert a mono stream to stereo by copying and interleaving samples */ -void AudioOutputUtil::MonoToStereo(void *dst, void *src, int samples) +void AudioOutputUtil::MonoToStereo(void *dst, const void *src, int samples) { - float *d = (float *)dst; - float *s = (float *)src; - for (int i = 0; i < samples; i++) - { - *d++ = *s; - *d++ = *s++; - } + AudioConvert::MonoToStereo(dst, src, samples); } /** @@ -754,25 +286,6 @@ int AudioOutputUtil::DecodeAudio(AVCodecContext *ctx, return ret; } -template -void _DeinterleaveSample(AudioDataType *out, const AudioDataType *in, int channels, int frames) -{ - AudioDataType *outp[8]; - - for (int i = 0; i < channels; i++) - { - outp[i] = out + (i * frames); - } - - for (int i = 0; i < frames; i++) - { - for (int j = 0; j < channels; j++) - { - *(outp[j]++) = *(in++); - } - } -} - /** * Deinterleave input samples * Deinterleave audio samples and compact them @@ -781,50 +294,7 @@ void AudioOutputUtil::DeinterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *input, int data_size) { - int bits = AudioOutputSettings::FormatToBits(format); - if (bits == 8) - { - _DeinterleaveSample((char *)output, (const char *)input, channels, data_size/sizeof(char)/channels); - } - else if (bits == 16) - { - _DeinterleaveSample((short *)output, (const short *)input, channels, data_size/sizeof(short)/channels); - } - else - { - _DeinterleaveSample((int *)output, (const int *)input, channels, data_size/sizeof(int)/channels); - } -} - -template -void _InterleaveSample(AudioDataType *out, const AudioDataType *in, int channels, int frames, - const AudioDataType * const *inp = NULL) -{ - const AudioDataType *my_inp[8]; - - if (!inp) - { - // We're given an array of int, calculate pointers to each row - for (int i = 0; i < channels; i++) - { - my_inp[i] = in + (i * frames); - } - } - else - { - for (int i = 0; i < channels; i++) - { - my_inp[i] = inp[i]; - } - } - - for (int i = 0; i < frames; i++) - { - for (int j = 0; j < channels; j++) - { - *(out++) = *(my_inp[j]++); - } - } + AudioConvert::DeinterleaveSamples(format, channels, output, input, data_size); } /** @@ -836,22 +306,7 @@ void AudioOutputUtil::InterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t * const *input, int data_size) { - int bits = AudioOutputSettings::FormatToBits(format); - if (bits == 8) - { - _InterleaveSample((char *)output, (const char *)NULL, channels, data_size/sizeof(char)/channels, - (const char * const *)input); - } - else if (bits == 16) - { - _InterleaveSample((short *)output, (const short *)NULL, channels, data_size/sizeof(short)/channels, - (const short * const *)input); - } - else - { - _InterleaveSample((int *)output, (const int *)NULL, channels, data_size/sizeof(int)/channels, - (const int * const *)input); - } + AudioConvert::InterleaveSamples(format, channels, output, input, data_size); } /** @@ -862,17 +317,5 @@ void AudioOutputUtil::InterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *input, int data_size) { - int bits = AudioOutputSettings::FormatToBits(format); - if (bits == 8) - { - _InterleaveSample((char *)output, (const char *)input, channels, data_size/sizeof(char)/channels); - } - else if (bits == 16) - { - _InterleaveSample((short *)output, (const short *)input, channels, data_size/sizeof(short)/channels); - } - else - { - _InterleaveSample((int *)output, (const int *)input, channels, data_size/sizeof(int)/channels); - } + AudioConvert::InterleaveSamples(format, channels, output, input, data_size); } diff --git a/mythtv/libs/libmyth/audio/audiooutpututil.h b/mythtv/libs/libmyth/audio/audiooutpututil.h index ae4ae79d8f4..f0f63a113d6 100644 --- a/mythtv/libs/libmyth/audio/audiooutpututil.h +++ b/mythtv/libs/libmyth/audio/audiooutpututil.h @@ -3,13 +3,16 @@ #include "audiooutputsettings.h" + +/** + * All toFloat variants require 16 byte aligned input and output buffers on x86 for SSE optimised operation + * The SSE code processes 16 bytes at a time and leaves any remainder for the C + * - there is no remainder in practice + */ class MPUBLIC AudioOutputUtil { public: static bool has_hardware_fpu(); - static int toFloat(AudioFormat format, void *out, void *in, int bytes); - static int fromFloat(AudioFormat format, void *out, void *in, int bytes); - static void MonoToStereo(void *dst, void *src, int samples); static void AdjustVolume(void *buffer, int len, int volume, bool music, bool upmix); static void MuteChannel(int obits, int channels, int ch, @@ -19,6 +22,11 @@ class MPUBLIC AudioOutputUtil static int DecodeAudio(AVCodecContext *ctx, uint8_t *buffer, int &data_size, const AVPacket *pkt); + + // Actually now in AudioConvert class, kept here for compatibility + static int toFloat(AudioFormat format, void *out, const void *in, int bytes); + static int fromFloat(AudioFormat format, void *out, const void *in, int bytes); + static void MonoToStereo(void *dst, const void *src, int samples); static void DeinterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *input, int data_size); diff --git a/mythtv/libs/libmyth/libmyth.pro b/mythtv/libs/libmyth/libmyth.pro index def78147e87..2a6f449495b 100644 --- a/mythtv/libs/libmyth/libmyth.pro +++ b/mythtv/libs/libmyth/libmyth.pro @@ -28,6 +28,7 @@ contains(INCLUDEPATH, /usr/local/include) { # Input HEADERS += audio/audiooutput.h audio/audiooutputbase.h audio/audiooutputnull.h HEADERS += audio/audiooutpututil.h audio/audiooutputdownmix.h +HEADERS += audio/audioconvert.h HEADERS += audio/audiooutputdigitalencoder.h audio/spdifencoder.h HEADERS += audio/audiosettings.h audio/audiooutputsettings.h audio/pink.h HEADERS += audio/volumebase.h audio/eldutils.h @@ -55,6 +56,7 @@ SOURCES += audio/audiooutput.cpp audio/audiooutputbase.cpp SOURCES += audio/spdifencoder.cpp audio/audiooutputdigitalencoder.cpp SOURCES += audio/audiooutputnull.cpp SOURCES += audio/audiooutpututil.cpp audio/audiooutputdownmix.cpp +SOURCES += audio/audioconvert.cpp SOURCES += audio/audiosettings.cpp audio/audiooutputsettings.cpp audio/pink.c SOURCES += audio/volumebase.cpp audio/eldutils.cpp @@ -121,6 +123,7 @@ inc.files += mythwidgets.h remotefile.h oldsettings.h volumecontrol.h inc.files += settings.h uitypes.h mythdialogs.h inc.files += audio/audiooutput.h audio/audiosettings.h inc.files += audio/audiooutputsettings.h audio/audiooutpututil.h +inc.files += audio/audioconvert.h inc.files += audio/volumebase.h audio/eldutils.h inc.files += inetcomms.h mythwizard.h schemawizard.h inc.files += mythmediamonitor.h diff --git a/mythtv/libs/libmyth/test/test_audioconvert/.gitignore b/mythtv/libs/libmyth/test/test_audioconvert/.gitignore new file mode 100644 index 00000000000..c4c9ad282a1 --- /dev/null +++ b/mythtv/libs/libmyth/test/test_audioconvert/.gitignore @@ -0,0 +1,5 @@ +test_audioutils +*.gcda +*.gcno +*.gcov + diff --git a/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.cpp b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.cpp new file mode 100644 index 00000000000..89315b80b79 --- /dev/null +++ b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.cpp @@ -0,0 +1,3 @@ +#include "test_audioconvert.h" + +QTEST_APPLESS_MAIN(TestAudioConvert) diff --git a/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.h b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.h new file mode 100644 index 00000000000..a091a211e5d --- /dev/null +++ b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.h @@ -0,0 +1,299 @@ +/* + * Class TestAudioConvert + * + * Copyright (C) Bubblestuff Pty Ltd 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "mythcorecontext.h" +#include "audioconvert.h" + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +#define MSKIP(MSG) QSKIP(MSG, SkipSingle) +#else +#define MSKIP(MSG) QSKIP(MSG) +#endif + +#define AOALIGN(x) (((long)&x + 15) & ~0xf); + +#define SSEALIGN 16 // for 16 bytes memory alignment + +#define ISIZEOF(type) ((int)sizeof(type)) + +class TestAudioConvert: public QObject +{ + Q_OBJECT + + private slots: + // called at the beginning of these sets of tests + void initTestCase(void) + { + gCoreContext = new MythCoreContext("bin_version", NULL); + } + + // test s16 -> float -> s16 + void Identical(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + short* arrays1 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + short* arrays2 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert ac = AudioConvert(FORMAT_S16, FORMAT_S16); + + int val1 = ac.Process(arrays2, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + av_free(arrays1); + av_free(arrays2); + } + + // test s16 -> float -> s16 is lossless + void S16ToFloat(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + short* arrays1 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + short* arrays2 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + float* arrayf = (float*)av_malloc(SIZEARRAY * ISIZEOF(float)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert acf = AudioConvert(FORMAT_S16, FORMAT_FLT); + AudioConvert acs = AudioConvert(FORMAT_FLT, FORMAT_S16); + + int val1 = acf.Process(arrayf, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrayf[0])); + int val2 = acs.Process(arrays2, arrayf, SIZEARRAY * ISIZEOF(arrayf[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrayf); + } + + // test S16 -> S24LSB -> S16 is lossless + void S16ToS24LSB(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + + short* arrays1 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + short* arrays2 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + int32_t* arrays24 = (int32_t*)av_malloc(SIZEARRAY * ISIZEOF(int32_t)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert ac24 = AudioConvert(FORMAT_S16, FORMAT_S24LSB); + AudioConvert acs = AudioConvert(FORMAT_S24LSB, FORMAT_S16); + + int val1 = ac24.Process(arrays24, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays24[0])); + int val2 = acs.Process(arrays2, arrays24, SIZEARRAY * ISIZEOF(arrays24[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + // Check we are indeed getting a 24 bits int + QVERIFY(arrays24[i] >= -(2<<23 + 1)); + QVERIFY(arrays24[i] <= (2<<23)); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays24); + } + + void S24LSBToS32(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + + int32_t* arrays1 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + int32_t* arrays2 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + int32_t* arrays32 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert ac24 = AudioConvert(FORMAT_S24LSB, FORMAT_S32); + AudioConvert acs = AudioConvert(FORMAT_S32, FORMAT_S24LSB); + + int val1 = ac24.Process(arrays32, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays32[0])); + int val2 = acs.Process(arrays2, arrays32, SIZEARRAY * ISIZEOF(arrays32[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays32); + } + + // test S16 -> S24 -> S16 is lossless + void S16ToS24(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + + short* arrays1 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + short* arrays2 = (short*)av_malloc(SIZEARRAY * ISIZEOF(short)); + int32_t* arrays24 = (int32_t*)av_malloc(SIZEARRAY * ISIZEOF(int32_t)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j << 8; + } + + AudioConvert ac24 = AudioConvert(FORMAT_S16, FORMAT_S24); + AudioConvert acs = AudioConvert(FORMAT_S24, FORMAT_S16); + + int val1 = ac24.Process(arrays24, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays24[0])); + int val2 = acs.Process(arrays2, arrays24, SIZEARRAY * ISIZEOF(arrays24[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + // Check we are indeed getting a 24 bits int + QCOMPARE(arrays24[i] & ~0xffff, arrays24[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays24); + } + + void S24ToS32(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + + int32_t* arrays1 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + int32_t* arrays2 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + int32_t* arrays32 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j << 8; + } + + AudioConvert ac32 = AudioConvert(FORMAT_S24, FORMAT_S32); + AudioConvert acs = AudioConvert(FORMAT_S32, FORMAT_S24); + + int val1 = ac32.Process(arrays32, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays32[0])); + int val2 = acs.Process(arrays2, arrays32, SIZEARRAY * ISIZEOF(arrays32[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays32); + } + + // test S16 -> S24 -> S16 is lossless + void S16ToS32(void) + { + int SIZEARRAY = (INT16_MAX - INT16_MIN); + + short* arrays1 = (short*)av_malloc((SIZEARRAY+1) * ISIZEOF(short)); + short* arrays2 = (short*)av_malloc((SIZEARRAY+1) * ISIZEOF(short)); + int32_t* arrays32 = (int32_t*)av_malloc((SIZEARRAY+1) * ISIZEOF(int32_t)); + + short j = INT16_MIN; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert ac32 = AudioConvert(FORMAT_S16, FORMAT_S32); + AudioConvert acs = AudioConvert(FORMAT_S32, FORMAT_S16); + + int val1 = ac32.Process(arrays32, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays32[0])); + int val2 = acs.Process(arrays2, arrays32, SIZEARRAY * ISIZEOF(arrays32[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays32); + } + + // test U8 -> S16 -> U8 is lossless + void U8ToS16(void) + { + int SIZEARRAY = 256; + + uchar* arrays1 = (uchar*)av_malloc((SIZEARRAY+1) * ISIZEOF(uchar)); + uchar* arrays2 = (uchar*)av_malloc((SIZEARRAY+1) * ISIZEOF(uchar)); + short* arrays32 = (short*)av_malloc((SIZEARRAY+1) * ISIZEOF(short)); + + uchar j = 0; + for (int i = 0; i < SIZEARRAY; i++, j++) + { + arrays1[i] = j; + } + + AudioConvert ac32 = AudioConvert(FORMAT_U8, FORMAT_S16); + AudioConvert acs = AudioConvert(FORMAT_S16, FORMAT_U8); + + int val1 = ac32.Process(arrays32, arrays1, SIZEARRAY * ISIZEOF(arrays1[0])); + QCOMPARE(val1, SIZEARRAY * ISIZEOF(arrays32[0])); + int val2 = acs.Process(arrays2, arrays32, SIZEARRAY * ISIZEOF(arrays32[0])); + QCOMPARE(val2, SIZEARRAY * ISIZEOF(arrays2[0])); + for (int i = 0; i < SIZEARRAY; i++) + { + QCOMPARE(arrays1[i], arrays2[i]); + } + + av_free(arrays1); + av_free(arrays2); + av_free(arrays32); + } +}; diff --git a/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.pro b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.pro new file mode 100644 index 00000000000..7825b7016ca --- /dev/null +++ b/mythtv/libs/libmyth/test/test_audioconvert/test_audioconvert.pro @@ -0,0 +1,44 @@ +include ( ../../../../settings.pro ) + +QT += xml sql network + +contains(QT_VERSION, ^4\\.[0-9]\\..*) { +CONFIG += qtestlib +} +contains(QT_VERSION, ^5\\.[0-9]\\..*) { +QT += testlib +} + +TEMPLATE = app +TARGET = test_audioconvert +DEPENDPATH += . ../.. ../../audio ../../logging ../../../libmythbase +INCLUDEPATH += . ../.. ../../audio ../../../../external/FFmpeg ../../logging ../../../libmythbase +LIBS += -L../.. -lmyth-$$LIBVERSION -L../../../libmythbase -lmythbase-$$LIBVERSION +LIBS += -L../../../../external/FFmpeg/libavutil -lmythavutil + +contains(QMAKE_CXX, "g++") { + QMAKE_CXXFLAGS += -O0 -fprofile-arcs -ftest-coverage + QMAKE_LFLAGS += -fprofile-arcs +} + +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/zeromq/src/.libs/ +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/nzmqt/src/ +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/qjson/lib/ +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/FFmpeg/libavcodec +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/FFmpeg/libavutil +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/FFmpeg/libavformat +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../../external/FFmpeg/libswresample +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../.. +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../libmythbase +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../libmythui +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../libmythupnp +QMAKE_LFLAGS += -Wl,$$_RPATH_$(PWD)/../../../libmythservicecontracts + +# Input +HEADERS += test_audioconvert.h +SOURCES += test_audioconvert.cpp + +QMAKE_CLEAN += $(TARGET) $(TARGETA) $(TARGETD) $(TARGET0) $(TARGET1) $(TARGET2) +QMAKE_CLEAN += ; rm -f *.gcov *.gcda *.gcno + +LIBS += $$EXTRA_LIBS $$LATE_LIBS diff --git a/mythtv/libs/libmyth/test/test_audioutils/test_audioutils.h b/mythtv/libs/libmyth/test/test_audioutils/test_audioutils.h index f40e85c43ec..447b2d3e9b6 100644 --- a/mythtv/libs/libmyth/test/test_audioutils/test_audioutils.h +++ b/mythtv/libs/libmyth/test/test_audioutils/test_audioutils.h @@ -319,7 +319,7 @@ class TestAudioUtils: public QObject } QFETCH(bool, useSSE); - + if (useSSE) { QBENCHMARK