MythTV master
audioconvert.cpp
Go to the documentation of this file.
1
2/*
3 * Class AudioConvert
4 * Created by Jean-Yves Avenard on 10/06/13.
5 *
6 * Copyright (C) Bubblestuff Pty Ltd 2013
7 * Copyright (C) foobum@gmail.com 2010
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23#include "audioconvert.h"
24
25#include <algorithm>
26#include <cmath>
27#include <cstdint>
28
29// FFmpeg
30extern "C" {
31#include "libavcodec/avcodec.h"
32#include "libswresample/swresample.h"
33}
34
35#include <QtGlobal>
36#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
37#include <QtProcessorDetection>
38#endif
39
41
43
44#define LOC QString("AudioConvert: ")
45
46#ifdef Q_PROCESSOR_X86
47// Check cpuid for SSE2 support on x86 / x86_64
48static inline bool sse2_check()
49{
50#ifdef Q_PROCESSOR_X86_64
51 return true;
52#endif
53 static int has_sse2 = -1;
54 if (has_sse2 != -1)
55 return (bool)has_sse2;
56 __asm__(
57 // -fPIC - we may not clobber ebx/rbx
58#ifdef Q_PROCESSOR_X86_64
59 "push %%rbx \n\t"
60#else
61 "push %%ebx \n\t"
62#endif
63 "mov $1, %%eax \n\t"
64 "cpuid \n\t"
65 "and $0x4000000, %%edx \n\t"
66 "shr $26, %%edx \n\t"
67#ifdef Q_PROCESSOR_X86_64
68 "pop %%rbx \n\t"
69#else
70 "pop %%ebx \n\t"
71#endif
72 :"=d"(has_sse2)
73 ::"%eax","%ecx"
74 );
75 return (bool)has_sse2;
76}
77#endif //Q_PROCESSOR_X86
78
79static inline float clipcheck(float f)
80{
81 return std::clamp(f, -1.0F, 1.0F);
82}
83
84/*
85 The SSE code processes 16 bytes at a time and leaves any remainder for the C
86 */
87
88static int toFloat8(float* out, const uint8_t* in, int len)
89{
90 int i = 0;
91 float f = 1.0F / (1<<7);
92
93#ifdef Q_PROCESSOR_X86
94 if (sse2_check() && len >= 16)
95 {
96 int loops = len >> 4;
97 i = loops << 4;
98 int a = 0x80808080;
99
100 __asm__ volatile (
101 "movd %3, %%xmm0 \n\t"
102 "movd %4, %%xmm7 \n\t"
103 "punpckldq %%xmm0, %%xmm0 \n\t"
104 "punpckldq %%xmm7, %%xmm7 \n\t"
105 "punpckldq %%xmm0, %%xmm0 \n\t"
106 "punpckldq %%xmm7, %%xmm7 \n\t"
107 "1: \n\t"
108 "movdqu (%1), %%xmm1 \n\t"
109 "xorpd %%xmm2, %%xmm2 \n\t"
110 "xorpd %%xmm3, %%xmm3 \n\t"
111 "psubb %%xmm0, %%xmm1 \n\t"
112 "xorpd %%xmm4, %%xmm4 \n\t"
113 "punpcklbw %%xmm1, %%xmm2 \n\t"
114 "xorpd %%xmm5, %%xmm5 \n\t"
115 "punpckhbw %%xmm1, %%xmm3 \n\t"
116 "punpcklwd %%xmm2, %%xmm4 \n\t"
117 "xorpd %%xmm6, %%xmm6 \n\t"
118 "punpckhwd %%xmm2, %%xmm5 \n\t"
119 "psrad $24, %%xmm4 \n\t"
120 "punpcklwd %%xmm3, %%xmm6 \n\t"
121 "psrad $24, %%xmm5 \n\t"
122 "punpckhwd %%xmm3, %%xmm1 \n\t"
123 "psrad $24, %%xmm6 \n\t"
124 "cvtdq2ps %%xmm4, %%xmm4 \n\t"
125 "psrad $24, %%xmm1 \n\t"
126 "cvtdq2ps %%xmm5, %%xmm5 \n\t"
127 "mulps %%xmm7, %%xmm4 \n\t"
128 "cvtdq2ps %%xmm6, %%xmm6 \n\t"
129 "mulps %%xmm7, %%xmm5 \n\t"
130 "movups %%xmm4, (%0) \n\t"
131 "cvtdq2ps %%xmm1, %%xmm1 \n\t"
132 "mulps %%xmm7, %%xmm6 \n\t"
133 "movups %%xmm5, 16(%0) \n\t"
134 "mulps %%xmm7, %%xmm1 \n\t"
135 "movups %%xmm6, 32(%0) \n\t"
136 "add $16, %1 \n\t"
137 "movups %%xmm1, 48(%0) \n\t"
138 "add $64, %0 \n\t"
139 "sub $1, %%ecx \n\t"
140 "jnz 1b \n\t"
141 :"+r"(out),"+r"(in)
142 :"c"(loops), "r"(a), "r"(f)
143 :"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"
144 );
145 }
146#endif //Q_PROCESSOR_X86
147 for (; i < len; i++)
148 *out++ = (*in++ - 0x80) * f;
149 return len << 2;
150}
151
152/*
153 The SSE code processes 16 bytes at a time and leaves any remainder for the C
154 - there is no remainder in practice */
155
156static inline uint8_t clip_uint8(long a)
157{
158 if (a&(~0xFF))
159 return (-a)>>31;
160 return a;
161}
162
163static int fromFloat8(uint8_t* out, const float* in, int len)
164{
165 int i = 0;
166 float f = (1<<7);
167
168#ifdef Q_PROCESSOR_X86
169 if (sse2_check() && len >= 16)
170 {
171 int loops = len >> 4;
172 i = loops << 4;
173 int a = 0x80808080;
174
175 __asm__ volatile (
176 "movd %3, %%xmm0 \n\t"
177 "movd %4, %%xmm7 \n\t"
178 "punpckldq %%xmm0, %%xmm0 \n\t"
179 "punpckldq %%xmm7, %%xmm7 \n\t"
180 "punpckldq %%xmm0, %%xmm0 \n\t"
181 "punpckldq %%xmm7, %%xmm7 \n\t"
182 "1: \n\t"
183 "movups (%1), %%xmm1 \n\t"
184 "movups 16(%1), %%xmm2 \n\t"
185 "mulps %%xmm7, %%xmm1 \n\t"
186 "movups 32(%1), %%xmm3 \n\t"
187 "mulps %%xmm7, %%xmm2 \n\t"
188 "cvtps2dq %%xmm1, %%xmm1 \n\t"
189 "movups 48(%1), %%xmm4 \n\t"
190 "mulps %%xmm7, %%xmm3 \n\t"
191 "cvtps2dq %%xmm2, %%xmm2 \n\t"
192 "mulps %%xmm7, %%xmm4 \n\t"
193 "cvtps2dq %%xmm3, %%xmm3 \n\t"
194 "packssdw %%xmm2, %%xmm1 \n\t"
195 "cvtps2dq %%xmm4, %%xmm4 \n\t"
196 "packssdw %%xmm4, %%xmm3 \n\t"
197 "add $64, %1 \n\t"
198 "packsswb %%xmm3, %%xmm1 \n\t"
199 "paddb %%xmm0, %%xmm1 \n\t"
200 "movdqu %%xmm1, (%0) \n\t"
201 "add $16, %0 \n\t"
202 "sub $1, %%ecx \n\t"
203 "jnz 1b \n\t"
204 :"+r"(out),"+r"(in)
205 :"c"(loops), "r"(a), "r"(f)
206 :"xmm0","xmm1","xmm2","xmm3","xmm4","xmm7"
207 );
208 }
209#endif //Q_PROCESSOR_X86
210 for (;i < len; i++)
211 *out++ = clip_uint8(std::lrintf(*in++ * f) + 0x80);
212 return len;
213}
214
215static int toFloat16(float* out, const short* in, int len)
216{
217 int i = 0;
218 float f = 1.0F / (1<<15);
219
220#ifdef Q_PROCESSOR_X86
221 if (sse2_check() && len >= 16)
222 {
223 int loops = len >> 4;
224 i = loops << 4;
225
226 __asm__ volatile (
227 "movd %3, %%xmm7 \n\t"
228 "punpckldq %%xmm7, %%xmm7 \n\t"
229 "punpckldq %%xmm7, %%xmm7 \n\t"
230 "1: \n\t"
231 "xorpd %%xmm2, %%xmm2 \n\t"
232 "movdqu (%1), %%xmm1 \n\t"
233 "xorpd %%xmm3, %%xmm3 \n\t"
234 "punpcklwd %%xmm1, %%xmm2 \n\t"
235 "movdqu 16(%1), %%xmm4 \n\t"
236 "punpckhwd %%xmm1, %%xmm3 \n\t"
237 "psrad $16, %%xmm2 \n\t"
238 "punpcklwd %%xmm4, %%xmm5 \n\t"
239 "psrad $16, %%xmm3 \n\t"
240 "cvtdq2ps %%xmm2, %%xmm2 \n\t"
241 "punpckhwd %%xmm4, %%xmm6 \n\t"
242 "psrad $16, %%xmm5 \n\t"
243 "mulps %%xmm7, %%xmm2 \n\t"
244 "cvtdq2ps %%xmm3, %%xmm3 \n\t"
245 "psrad $16, %%xmm6 \n\t"
246 "mulps %%xmm7, %%xmm3 \n\t"
247 "cvtdq2ps %%xmm5, %%xmm5 \n\t"
248 "movups %%xmm2, (%0) \n\t"
249 "cvtdq2ps %%xmm6, %%xmm6 \n\t"
250 "mulps %%xmm7, %%xmm5 \n\t"
251 "movups %%xmm3, 16(%0) \n\t"
252 "mulps %%xmm7, %%xmm6 \n\t"
253 "movups %%xmm5, 32(%0) \n\t"
254 "add $32, %1 \n\t"
255 "movups %%xmm6, 48(%0) \n\t"
256 "add $64, %0 \n\t"
257 "sub $1, %%ecx \n\t"
258 "jnz 1b \n\t"
259 :"+r"(out),"+r"(in)
260 :"c"(loops), "r"(f)
261 :"xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"
262 );
263 }
264#endif //Q_PROCESSOR_X86
265 for (; i < len; i++)
266 *out++ = *in++ * f;
267 return len << 2;
268}
269
270static inline short clip_short(long a)
271{
272 if ((a+0x8000) & ~0xFFFF)
273 return (a>>31) ^ 0x7FFF;
274 return a;
275}
276
277static int fromFloat16(short* out, const float* in, int len)
278{
279 int i = 0;
280 float f = (1<<15);
281
282#ifdef Q_PROCESSOR_X86
283 if (sse2_check() && len >= 16)
284 {
285 int loops = len >> 4;
286 i = loops << 4;
287
288 __asm__ volatile (
289 "movd %3, %%xmm7 \n\t"
290 "punpckldq %%xmm7, %%xmm7 \n\t"
291 "punpckldq %%xmm7, %%xmm7 \n\t"
292 "1: \n\t"
293 "movups (%1), %%xmm1 \n\t"
294 "movups 16(%1), %%xmm2 \n\t"
295 "mulps %%xmm7, %%xmm1 \n\t"
296 "movups 32(%1), %%xmm3 \n\t"
297 "mulps %%xmm7, %%xmm2 \n\t"
298 "cvtps2dq %%xmm1, %%xmm1 \n\t"
299 "movups 48(%1), %%xmm4 \n\t"
300 "mulps %%xmm7, %%xmm3 \n\t"
301 "cvtps2dq %%xmm2, %%xmm2 \n\t"
302 "mulps %%xmm7, %%xmm4 \n\t"
303 "cvtps2dq %%xmm3, %%xmm3 \n\t"
304 "cvtps2dq %%xmm4, %%xmm4 \n\t"
305 "packssdw %%xmm2, %%xmm1 \n\t"
306 "packssdw %%xmm4, %%xmm3 \n\t"
307 "add $64, %1 \n\t"
308 "movdqu %%xmm1, (%0) \n\t"
309 "movdqu %%xmm3, 16(%0) \n\t"
310 "add $32, %0 \n\t"
311 "sub $1, %%ecx \n\t"
312 "jnz 1b \n\t"
313 :"+r"(out),"+r"(in)
314 :"c"(loops), "r"(f)
315 :"xmm1","xmm2","xmm3","xmm4","xmm7"
316 );
317 }
318#endif //Q_PROCESSOR_X86
319 for (;i < len;i++)
320 *out++ = clip_short(std::lrintf(*in++ * f));
321 return len << 1;
322}
323
324static int toFloat32(AudioFormat format, float* out, const int* in, int len)
325{
326 int i = 0;
327 int bits = AudioOutputSettings::FormatToBits(format);
328 float f = 1.0F / ((uint)(1<<(bits-1)));
329 int shift = 32 - bits;
330
331 if (format == FORMAT_S24LSB)
332 shift = 0;
333
334#ifdef Q_PROCESSOR_X86
335 if (sse2_check() && len >= 16)
336 {
337 int loops = len >> 4;
338 i = loops << 4;
339
340 __asm__ volatile (
341 "movd %3, %%xmm7 \n\t"
342 "punpckldq %%xmm7, %%xmm7 \n\t"
343 "movd %4, %%xmm6 \n\t"
344 "punpckldq %%xmm7, %%xmm7 \n\t"
345 "1: \n\t"
346 "movdqu (%1), %%xmm1 \n\t"
347 "movdqu 16(%1), %%xmm2 \n\t"
348 "psrad %%xmm6, %%xmm1 \n\t"
349 "movdqu 32(%1), %%xmm3 \n\t"
350 "cvtdq2ps %%xmm1, %%xmm1 \n\t"
351 "psrad %%xmm6, %%xmm2 \n\t"
352 "movdqu 48(%1), %%xmm4 \n\t"
353 "cvtdq2ps %%xmm2, %%xmm2 \n\t"
354 "psrad %%xmm6, %%xmm3 \n\t"
355 "mulps %%xmm7, %%xmm1 \n\t"
356 "psrad %%xmm6, %%xmm4 \n\t"
357 "cvtdq2ps %%xmm3, %%xmm3 \n\t"
358 "movups %%xmm1, (%0) \n\t"
359 "mulps %%xmm7, %%xmm2 \n\t"
360 "cvtdq2ps %%xmm4, %%xmm4 \n\t"
361 "movups %%xmm2, 16(%0) \n\t"
362 "mulps %%xmm7, %%xmm3 \n\t"
363 "mulps %%xmm7, %%xmm4 \n\t"
364 "movups %%xmm3, 32(%0) \n\t"
365 "add $64, %1 \n\t"
366 "movups %%xmm4, 48(%0) \n\t"
367 "add $64, %0 \n\t"
368 "sub $1, %%ecx \n\t"
369 "jnz 1b \n\t"
370 :"+r"(out),"+r"(in)
371 :"c"(loops), "r"(f), "r"(shift)
372 :"xmm1","xmm2","xmm3","xmm4","xmm6","xmm7"
373 );
374 }
375#endif //Q_PROCESSOR_X86
376 for (; i < len; i++)
377 *out++ = (*in++ >> shift) * f;
378 return len << 2;
379}
380
381static int fromFloat32(AudioFormat format, int* out, const float* in, int len)
382{
383 int i = 0;
384 int bits = AudioOutputSettings::FormatToBits(format);
385 float f = (uint)(1<<(bits-1));
386 int shift = 32 - bits;
387
388 if (format == FORMAT_S24LSB)
389 shift = 0;
390
391#ifdef Q_PROCESSOR_X86
392 if (sse2_check() && len >= 16)
393 {
394 float o = 0.99999995;
395 float mo = -1;
396 int loops = len >> 4;
397 i = loops << 4;
398
399 __asm__ volatile (
400 "movd %3, %%xmm7 \n\t"
401 "movss %4, %%xmm5 \n\t"
402 "punpckldq %%xmm7, %%xmm7 \n\t"
403 "movss %5, %%xmm6 \n\t"
404 "punpckldq %%xmm5, %%xmm5 \n\t"
405 "punpckldq %%xmm6, %%xmm6 \n\t"
406 "movd %6, %%xmm0 \n\t"
407 "punpckldq %%xmm7, %%xmm7 \n\t"
408 "punpckldq %%xmm5, %%xmm5 \n\t"
409 "punpckldq %%xmm6, %%xmm6 \n\t"
410 "1: \n\t"
411 "movups (%1), %%xmm1 \n\t"
412 "movups 16(%1), %%xmm2 \n\t"
413 "minps %%xmm5, %%xmm1 \n\t"
414 "movups 32(%1), %%xmm3 \n\t"
415 "maxps %%xmm6, %%xmm1 \n\t"
416 "movups 48(%1), %%xmm4 \n\t"
417 "mulps %%xmm7, %%xmm1 \n\t"
418 "minps %%xmm5, %%xmm2 \n\t"
419 "cvtps2dq %%xmm1, %%xmm1 \n\t"
420 "maxps %%xmm6, %%xmm2 \n\t"
421 "pslld %%xmm0, %%xmm1 \n\t"
422 "minps %%xmm5, %%xmm3 \n\t"
423 "mulps %%xmm7, %%xmm2 \n\t"
424 "movdqu %%xmm1, (%0) \n\t"
425 "cvtps2dq %%xmm2, %%xmm2 \n\t"
426 "maxps %%xmm6, %%xmm3 \n\t"
427 "minps %%xmm5, %%xmm4 \n\t"
428 "pslld %%xmm0, %%xmm2 \n\t"
429 "mulps %%xmm7, %%xmm3 \n\t"
430 "maxps %%xmm6, %%xmm4 \n\t"
431 "movdqu %%xmm2, 16(%0) \n\t"
432 "cvtps2dq %%xmm3, %%xmm3 \n\t"
433 "mulps %%xmm7, %%xmm4 \n\t"
434 "pslld %%xmm0, %%xmm3 \n\t"
435 "cvtps2dq %%xmm4, %%xmm4 \n\t"
436 "movdqu %%xmm3, 32(%0) \n\t"
437 "pslld %%xmm0, %%xmm4 \n\t"
438 "add $64, %1 \n\t"
439 "movdqu %%xmm4, 48(%0) \n\t"
440 "add $64, %0 \n\t"
441 "sub $1, %%ecx \n\t"
442 "jnz 1b \n\t"
443 :"+r"(out), "+r"(in)
444 :"c"(loops), "r"(f), "m"(o), "m"(mo), "r"(shift)
445 :"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"
446 );
447 }
448#endif //Q_PROCESSOR_X86
449 uint range = 1<<(bits-1);
450 for (; i < len; i++)
451 {
452 float valf = *in++;
453
454 if (valf >= 1.0F)
455 {
456 *out++ = (range - 128) << shift;
457 continue;
458 }
459 if (valf <= -1.0F)
460 {
461 *out++ = (-range) << shift;
462 continue;
463 }
464 *out++ = std::lrintf(valf * f) << shift;
465 }
466 return len << 2;
467}
468
469static int fromFloatFLT(float* out, const float* in, int len)
470{
471 int i = 0;
472
473#ifdef Q_PROCESSOR_X86
474 if (sse2_check() && len >= 16)
475 {
476 int loops = len >> 4;
477 float o = 1;
478 float mo = -1;
479 i = loops << 4;
480
481 __asm__ volatile (
482 "movss %3, %%xmm6 \n\t"
483 "movss %4, %%xmm7 \n\t"
484 "punpckldq %%xmm6, %%xmm6 \n\t"
485 "punpckldq %%xmm7, %%xmm7 \n\t"
486 "punpckldq %%xmm6, %%xmm6 \n\t"
487 "punpckldq %%xmm7, %%xmm7 \n\t"
488 "1: \n\t"
489 "movups (%1), %%xmm1 \n\t"
490 "movups 16(%1), %%xmm2 \n\t"
491 "minps %%xmm6, %%xmm1 \n\t"
492 "movups 32(%1), %%xmm3 \n\t"
493 "maxps %%xmm7, %%xmm1 \n\t"
494 "minps %%xmm6, %%xmm2 \n\t"
495 "movups 48(%1), %%xmm4 \n\t"
496 "maxps %%xmm7, %%xmm2 \n\t"
497 "movups %%xmm1, (%0) \n\t"
498 "minps %%xmm6, %%xmm3 \n\t"
499 "movups %%xmm2, 16(%0) \n\t"
500 "maxps %%xmm7, %%xmm3 \n\t"
501 "minps %%xmm6, %%xmm4 \n\t"
502 "movups %%xmm3, 32(%0) \n\t"
503 "maxps %%xmm7, %%xmm4 \n\t"
504 "add $64, %1 \n\t"
505 "movups %%xmm4, 48(%0) \n\t"
506 "add $64, %0 \n\t"
507 "sub $1, %%ecx \n\t"
508 "jnz 1b \n\t"
509 :"+r"(out), "+r"(in)
510 :"c"(loops), "m"(o), "m"(mo)
511 :"xmm1","xmm2","xmm3","xmm4","xmm6","xmm7"
512 );
513 }
514#endif //Q_PROCESSOR_X86
515 for (;i < len;i++)
516 *out++ = clipcheck(*in++);
517 return len << 2;
518}
519
525int AudioConvert::toFloat(AudioFormat format, void* out, const void* in,
526 int bytes)
527{
528 if (bytes <= 0)
529 return 0;
530
531 switch (format)
532 {
533 case FORMAT_U8:
534 return toFloat8((float*)out, (uint8_t*)in, bytes);
535 case FORMAT_S16:
536 return toFloat16((float*)out, (short*)in, bytes >> 1);
537 case FORMAT_S24:
538 case FORMAT_S24LSB:
539 case FORMAT_S32:
540 return toFloat32(format, (float*)out, (int*)in, bytes >> 2);
541 case FORMAT_FLT:
542 memcpy(out, in, bytes);
543 return bytes;
544 case FORMAT_NONE:
545 default:
546 return 0;
547 }
548}
549
555int AudioConvert::fromFloat(AudioFormat format, void* out, const void* in,
556 int bytes)
557{
558 if (bytes <= 0)
559 return 0;
560
561 switch (format)
562 {
563 case FORMAT_U8:
564 return fromFloat8((uint8_t*)out, (float*)in, bytes >> 2);
565 case FORMAT_S16:
566 return fromFloat16((short*)out, (float*)in, bytes >> 2);
567 case FORMAT_S24:
568 case FORMAT_S24LSB:
569 case FORMAT_S32:
570 return fromFloat32(format, (int*)out, (float*)in, bytes >> 2);
571 case FORMAT_FLT:
572 return fromFloatFLT((float*)out, (float*)in, bytes >> 2);
573 case FORMAT_NONE:
574 default:
575 return 0;
576 }
577}
578
580
582{
583public:
584 AudioConvertInternal(AVSampleFormat in, AVSampleFormat out) :
585 m_in(in), m_out(out)
586 {
587 AVChannelLayout channel_layout;
588 av_channel_layout_default(&channel_layout, 1);
589 int ret = swr_alloc_set_opts2(&m_swr,
590 &channel_layout,
591 m_out,
592 48000,
593 &channel_layout,
594 m_in,
595 48000,
596 0, nullptr);
597 if (!m_swr || ret < 0)
598 {
599 std::string error;
600 LOG(VB_AUDIO, LOG_ERR, LOC +
601 QString("error allocating resampler context (%1)")
602 .arg(av_make_error_stdstring(error, ret)));
603 return;
604 }
605 /* initialize the resampling context */
606 ret = swr_init(m_swr);
607 if (ret < 0)
608 {
609 std::string error;
610 LOG(VB_AUDIO, LOG_ERR, LOC +
611 QString("error initializing resampler context (%1)")
612 .arg(av_make_error_stdstring(error, ret)));
613 swr_free(&m_swr);
614 return;
615 }
616 }
617 int Process(void* out, const void* in, int bytes) const
618 {
619 if (!m_swr)
620 return -1;
621
622 std::array<uint8_t*,1> outp {(uint8_t*)out};
623 std::array<const uint8_t*,1> inp {(const uint8_t*)in};
624 int samples = bytes / av_get_bytes_per_sample(m_in);
625 int ret = swr_convert(m_swr,
626 outp.data(), samples,
627 inp.data(), samples);
628 if (ret < 0)
629 return ret;
630 return ret * av_get_bytes_per_sample(m_out);
631 }
633 {
634 if (m_swr)
635 {
636 swr_free(&m_swr);
637 }
638 }
639
640 SwrContext* m_swr {nullptr};
641 AVSampleFormat m_in, m_out;
642};
643
644
646{
647 delete m_ctx;
648 m_ctx = nullptr;
649}
650
657int AudioConvert::Process(void* out, const void* in, int bytes, bool noclip)
658{
659 if (bytes <= 0)
660 return 0;
661 if (m_out == FORMAT_NONE || m_in == FORMAT_NONE)
662 return 0;
663
664 if (noclip && m_in == m_out)
665 {
666 memcpy(out, in, bytes);
667 return bytes;
668 }
669
670 /* use conversion routines to perform clipping on samples */
671 if (m_in == FORMAT_FLT)
672 return fromFloat(m_out, out, in, bytes);
673 if (m_out == FORMAT_FLT)
674 return toFloat(m_in, out, in, bytes);
675
676 if (m_in == m_out)
677 {
678 memcpy(out, in, bytes);
679 return bytes;
680 }
681
682 if (m_in == FORMAT_S24 || m_in == FORMAT_S24LSB ||
684 {
685 // FFmpeg can't handle those, so use float conversion intermediary
687 {
688 // this can be done in place
689 int s = toFloat(m_in, out, in, bytes);
690 return fromFloat(m_out, out, out, s);
691 }
692 // this leave S24 -> U8/S16.
693 // TODO: native handling of those ; use internal temp buffer in the mean time
694
695 alignas(16) std::array<uint8_t,65536> buffer {0};
696 int left = bytes;
697
698 while (left > 0)
699 {
700 int s = 0;
701
702 if (left >= 65536)
703 {
704 s = toFloat(m_in, buffer.data(), in, buffer.size());
705 in = static_cast<const uint8_t *>(in) + s;
706 out = static_cast<uint8_t *>(out) + fromFloat(m_out, out, buffer.data(), s);
707 left -= buffer.size();
708 continue;
709 }
710 s = toFloat(m_in, buffer.data(), in, left);
711 in = static_cast<const uint8_t *>(in) + s;
712 out = static_cast<uint8_t *>(out) + fromFloat(m_out, out, buffer.data(), s);
713 left = 0;
714 }
716 }
717
718 // use FFmpeg conversion routine for S32<->S16, S32<->U8 and S16<->U8
719 if (!m_ctx)
720 {
723 }
724
725 return m_ctx->Process(out, in, bytes);
726}
727
731void AudioConvert::MonoToStereo(void* dst, const void* src, int samples)
732{
733 auto* d = (float*)dst;
734 auto* s = (float*)src;
735 for (int i = 0; i < samples; i++)
736 {
737 *d++ = *s;
738 *d++ = *s++;
739 }
740}
741
742template <class AudioDataType>
743void tDeinterleaveSample(AudioDataType* out, const AudioDataType* in, int channels, int frames)
744{
745 std::array<AudioDataType*,8> outp {};
746
747 for (int i = 0; i < channels; i++)
748 {
749 outp[i] = out + (i * frames);
750 }
751
752 for (int i = 0; i < frames; i++)
753 {
754 for (int j = 0; j < channels; j++)
755 {
756 *(outp[j]++) = *(in++);
757 }
758 }
759}
760
766 uint8_t* output, const uint8_t* input,
767 int data_size)
768{
769 if (channels == 1)
770 {
771 // If channel count is 1, planar and non-planar formats are the same
772 memcpy(output, input, data_size);
773 return;
774 }
775
776 int bits = AudioOutputSettings::FormatToBits(format);
777 if (bits == 8)
778 {
779 tDeinterleaveSample((char*)output, (const char*)input, channels, data_size/sizeof(char)/channels);
780 }
781 else if (bits == 16)
782 {
783 tDeinterleaveSample((short*)output, (const short*)input, channels, data_size/sizeof(short)/channels);
784 }
785 else
786 {
787 tDeinterleaveSample((int*)output, (const int*)input, channels, data_size/sizeof(int)/channels);
788 }
789}
790
791template <class AudioDataType>
792void tInterleaveSample(AudioDataType* out, const AudioDataType* in, int channels, int frames,
793 const AudioDataType* const* inp = nullptr)
794{
795 std::array<const AudioDataType*,8> my_inp {};
796
797 if (channels == 1)
798 {
799 //special case for mono
800 memcpy(out, inp ? inp[0] : in, sizeof(AudioDataType) * frames);
801 return;
802 }
803
804 if (!inp)
805 {
806 // We're given an array of int, calculate pointers to each row
807 for (int i = 0; i < channels; i++)
808 {
809 my_inp[i] = in + (i * frames);
810 }
811 }
812 else
813 {
814 for (int i = 0; i < channels; i++)
815 {
816 my_inp[i] = inp[i];
817 }
818 }
819
820 for (int i = 0; i < frames; i++)
821 {
822 for (int j = 0; j < channels; j++)
823 {
824 *(out++) = *(my_inp[j]++);
825 }
826 }
827}
828
835 uint8_t* output, const uint8_t* const* input,
836 int data_size)
837{
838 int bits = AudioOutputSettings::FormatToBits(format);
839 if (bits == 8)
840 {
841 tInterleaveSample((char*)output, (const char*)nullptr, channels, data_size/sizeof(char)/channels,
842 (const char* const*)input);
843 }
844 else if (bits == 16)
845 {
846 tInterleaveSample((short*)output, (const short*)nullptr, channels, data_size/sizeof(short)/channels,
847 (const short* const*)input);
848 }
849 else
850 {
851 tInterleaveSample((int*)output, (const int*)nullptr, channels, data_size/sizeof(int)/channels,
852 (const int* const*)input);
853 }
854}
855
861 uint8_t* output, const uint8_t* input,
862 int data_size)
863{
864 int bits = AudioOutputSettings::FormatToBits(format);
865 if (bits == 8)
866 {
867 tInterleaveSample((char*)output, (const char*)input, channels, data_size/sizeof(char)/channels);
868 }
869 else if (bits == 16)
870 {
871 tInterleaveSample((short*)output, (const short*)input, channels, data_size/sizeof(short)/channels);
872 }
873 else
874 {
875 tInterleaveSample((int*)output, (const int*)input, channels, data_size/sizeof(int)/channels);
876 }
877}
878
880 uint8_t* output, const uint8_t* input,
881 int data_size)
882{
883 DeinterleaveSamples(m_in, channels, output, input, data_size);
884}
885
887 uint8_t* output, const uint8_t* const* input,
888 int data_size)
889{
890 InterleaveSamples(m_in, channels, output, input, data_size);
891}
892
894 uint8_t* output, const uint8_t* input,
895 int data_size)
896{
897 InterleaveSamples(m_in, channels, output, input, data_size);
898}
#define LOC
void tDeinterleaveSample(AudioDataType *out, const AudioDataType *in, int channels, int frames)
static int fromFloatFLT(float *out, const float *in, int len)
static uint8_t clip_uint8(long a)
static float clipcheck(float f)
static int toFloat8(float *out, const uint8_t *in, int len)
static int fromFloat16(short *out, const float *in, int len)
static int fromFloat32(AudioFormat format, int *out, const float *in, int len)
static int toFloat16(float *out, const short *in, int len)
static int toFloat32(AudioFormat format, float *out, const int *in, int len)
void tInterleaveSample(AudioDataType *out, const AudioDataType *in, int channels, int frames, const AudioDataType *const *inp=nullptr)
static short clip_short(long a)
static int fromFloat8(uint8_t *out, const float *in, int len)
@ FORMAT_U8
@ FORMAT_S32
@ FORMAT_S24
@ FORMAT_NONE
@ FORMAT_FLT
@ FORMAT_S16
@ FORMAT_S24LSB
AVSampleFormat m_out
int Process(void *out, const void *in, int bytes) const
AVSampleFormat m_in
AudioConvertInternal(AVSampleFormat in, AVSampleFormat out)
static int toFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert integer samples to floats.
virtual ~AudioConvert()
AudioConvertInternal * m_ctx
Definition: audioconvert.h:81
void DeinterleaveSamples(int channels, uint8_t *output, const uint8_t *input, int data_size)
static void MonoToStereo(void *dst, const void *src, int samples)
Convert a mono stream to stereo by copying and interleaving samples.
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
void InterleaveSamples(int channels, uint8_t *output, const uint8_t *const *input, int data_size)
AudioFormat m_in
Definition: audioconvert.h:82
AudioFormat m_out
Definition: audioconvert.h:82
int Process(void *out, const void *in, int bytes, bool noclip=false)
Process Parameters: out : destination buffer where converted samples will be copied in : source buffe...
static int SampleSize(AudioFormat format)
static AVSampleFormat FormatToAVSampleFormat(AudioFormat format)
Return AudioFormat closest equivalent to AVSampleFormat Note that FORMAT_S24LSB and FORMAT_S24 have n...
static int FormatToBits(AudioFormat format)
unsigned int uint
Definition: compat.h:60
static const std::array< const uint64_t, 4 > samples
Definition: element.cpp:46
static const iso6937table * d
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
Definition: mythaverror.cpp:42
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204
#define output