Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (C) 2011-2012 Michael Niedermayer (michaelni@gmx.at) | |
3 | * Copyright (c) 2002 Fabrice Bellard | |
4 | * | |
5 | * This file is part of libswresample | |
6 | * | |
7 | * libswresample is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 2 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * libswresample is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with libswresample; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | #include "libavutil/avassert.h" | |
23 | #include "libavutil/channel_layout.h" | |
24 | #include "libavutil/common.h" | |
25 | #include "libavutil/opt.h" | |
26 | #include "swresample.h" | |
27 | ||
28 | #undef time | |
29 | #include "time.h" | |
30 | #undef fprintf | |
31 | ||
32 | #define SAMPLES 1000 | |
33 | ||
34 | #define SWR_CH_MAX 32 | |
35 | ||
36 | #define ASSERT_LEVEL 2 | |
37 | ||
38 | static double get(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f){ | |
39 | const uint8_t *p; | |
40 | if(av_sample_fmt_is_planar(f)){ | |
41 | f= av_get_alt_sample_fmt(f, 0); | |
42 | p= a[ch]; | |
43 | }else{ | |
44 | p= a[0]; | |
45 | index= ch + index*ch_count; | |
46 | } | |
47 | ||
48 | switch(f){ | |
49 | case AV_SAMPLE_FMT_U8 : return ((const uint8_t*)p)[index]/127.0-1.0; | |
50 | case AV_SAMPLE_FMT_S16: return ((const int16_t*)p)[index]/32767.0; | |
51 | case AV_SAMPLE_FMT_S32: return ((const int32_t*)p)[index]/2147483647.0; | |
52 | case AV_SAMPLE_FMT_FLT: return ((const float *)p)[index]; | |
53 | case AV_SAMPLE_FMT_DBL: return ((const double *)p)[index]; | |
54 | default: av_assert0(0); | |
55 | } | |
56 | } | |
57 | ||
58 | static void set(uint8_t *a[], int ch, int index, int ch_count, enum AVSampleFormat f, double v){ | |
59 | uint8_t *p; | |
60 | if(av_sample_fmt_is_planar(f)){ | |
61 | f= av_get_alt_sample_fmt(f, 0); | |
62 | p= a[ch]; | |
63 | }else{ | |
64 | p= a[0]; | |
65 | index= ch + index*ch_count; | |
66 | } | |
67 | switch(f){ | |
68 | case AV_SAMPLE_FMT_U8 : ((uint8_t*)p)[index]= av_clip_uint8 (lrint((v+1.0)*127)); break; | |
69 | case AV_SAMPLE_FMT_S16: ((int16_t*)p)[index]= av_clip_int16 (lrint(v*32767)); break; | |
70 | case AV_SAMPLE_FMT_S32: ((int32_t*)p)[index]= av_clipl_int32(llrint(v*2147483647)); break; | |
71 | case AV_SAMPLE_FMT_FLT: ((float *)p)[index]= v; break; | |
72 | case AV_SAMPLE_FMT_DBL: ((double *)p)[index]= v; break; | |
73 | default: av_assert2(0); | |
74 | } | |
75 | } | |
76 | ||
77 | static void shift(uint8_t *a[], int index, int ch_count, enum AVSampleFormat f){ | |
78 | int ch; | |
79 | ||
80 | if(av_sample_fmt_is_planar(f)){ | |
81 | f= av_get_alt_sample_fmt(f, 0); | |
82 | for(ch= 0; ch<ch_count; ch++) | |
83 | a[ch] += index*av_get_bytes_per_sample(f); | |
84 | }else{ | |
85 | a[0] += index*ch_count*av_get_bytes_per_sample(f); | |
86 | } | |
87 | } | |
88 | ||
89 | static const enum AVSampleFormat formats[] = { | |
90 | AV_SAMPLE_FMT_S16, | |
91 | AV_SAMPLE_FMT_FLTP, | |
92 | AV_SAMPLE_FMT_S16P, | |
93 | AV_SAMPLE_FMT_FLT, | |
94 | AV_SAMPLE_FMT_S32P, | |
95 | AV_SAMPLE_FMT_S32, | |
96 | AV_SAMPLE_FMT_U8P, | |
97 | AV_SAMPLE_FMT_U8, | |
98 | AV_SAMPLE_FMT_DBLP, | |
99 | AV_SAMPLE_FMT_DBL, | |
100 | }; | |
101 | ||
102 | static const int rates[] = { | |
103 | 8000, | |
104 | 11025, | |
105 | 16000, | |
106 | 22050, | |
107 | 32000, | |
108 | 48000, | |
109 | }; | |
110 | ||
111 | uint64_t layouts[]={ | |
112 | AV_CH_LAYOUT_MONO , | |
113 | AV_CH_LAYOUT_STEREO , | |
114 | AV_CH_LAYOUT_2_1 , | |
115 | AV_CH_LAYOUT_SURROUND , | |
116 | AV_CH_LAYOUT_4POINT0 , | |
117 | AV_CH_LAYOUT_2_2 , | |
118 | AV_CH_LAYOUT_QUAD , | |
119 | AV_CH_LAYOUT_5POINT0 , | |
120 | AV_CH_LAYOUT_5POINT1 , | |
121 | AV_CH_LAYOUT_5POINT0_BACK , | |
122 | AV_CH_LAYOUT_5POINT1_BACK , | |
123 | AV_CH_LAYOUT_7POINT0 , | |
124 | AV_CH_LAYOUT_7POINT1 , | |
125 | AV_CH_LAYOUT_7POINT1_WIDE , | |
126 | }; | |
127 | ||
128 | static void setup_array(uint8_t *out[SWR_CH_MAX], uint8_t *in, enum AVSampleFormat format, int samples){ | |
129 | if(av_sample_fmt_is_planar(format)){ | |
130 | int i; | |
131 | int plane_size= av_get_bytes_per_sample(format&0xFF)*samples; | |
132 | format&=0xFF; | |
133 | for(i=0; i<SWR_CH_MAX; i++){ | |
134 | out[i]= in + i*plane_size; | |
135 | } | |
136 | }else{ | |
137 | out[0]= in; | |
138 | } | |
139 | } | |
140 | ||
141 | static int cmp(const int *a, const int *b){ | |
142 | return *a - *b; | |
143 | } | |
144 | ||
145 | static void audiogen(void *data, enum AVSampleFormat sample_fmt, | |
146 | int channels, int sample_rate, int nb_samples) | |
147 | { | |
148 | int i, ch, k; | |
149 | double v, f, a, ampa; | |
150 | double tabf1[SWR_CH_MAX]; | |
151 | double tabf2[SWR_CH_MAX]; | |
152 | double taba[SWR_CH_MAX]; | |
153 | unsigned static rnd; | |
154 | ||
155 | #define PUT_SAMPLE set(data, ch, k, channels, sample_fmt, v); | |
156 | #define uint_rand(x) (x = x * 1664525 + 1013904223) | |
157 | #define dbl_rand(x) (uint_rand(x)*2.0 / (double)UINT_MAX - 1) | |
158 | k = 0; | |
159 | ||
160 | /* 1 second of single freq sinus at 1000 Hz */ | |
161 | a = 0; | |
162 | for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { | |
163 | v = sin(a) * 0.30; | |
164 | for (ch = 0; ch < channels; ch++) | |
165 | PUT_SAMPLE | |
166 | a += M_PI * 1000.0 * 2.0 / sample_rate; | |
167 | } | |
168 | ||
169 | /* 1 second of varying frequency between 100 and 10000 Hz */ | |
170 | a = 0; | |
171 | for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { | |
172 | v = sin(a) * 0.30; | |
173 | for (ch = 0; ch < channels; ch++) | |
174 | PUT_SAMPLE | |
175 | f = 100.0 + (((10000.0 - 100.0) * i) / sample_rate); | |
176 | a += M_PI * f * 2.0 / sample_rate; | |
177 | } | |
178 | ||
179 | /* 0.5 second of low amplitude white noise */ | |
180 | for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { | |
181 | v = dbl_rand(rnd) * 0.30; | |
182 | for (ch = 0; ch < channels; ch++) | |
183 | PUT_SAMPLE | |
184 | } | |
185 | ||
186 | /* 0.5 second of high amplitude white noise */ | |
187 | for (i = 0; i < sample_rate / 2 && k < nb_samples; i++, k++) { | |
188 | v = dbl_rand(rnd); | |
189 | for (ch = 0; ch < channels; ch++) | |
190 | PUT_SAMPLE | |
191 | } | |
192 | ||
193 | /* 1 second of unrelated ramps for each channel */ | |
194 | for (ch = 0; ch < channels; ch++) { | |
195 | taba[ch] = 0; | |
196 | tabf1[ch] = 100 + uint_rand(rnd) % 5000; | |
197 | tabf2[ch] = 100 + uint_rand(rnd) % 5000; | |
198 | } | |
199 | for (i = 0; i < 1 * sample_rate && k < nb_samples; i++, k++) { | |
200 | for (ch = 0; ch < channels; ch++) { | |
201 | v = sin(taba[ch]) * 0.30; | |
202 | PUT_SAMPLE | |
203 | f = tabf1[ch] + (((tabf2[ch] - tabf1[ch]) * i) / sample_rate); | |
204 | taba[ch] += M_PI * f * 2.0 / sample_rate; | |
205 | } | |
206 | } | |
207 | ||
208 | /* 2 seconds of 500 Hz with varying volume */ | |
209 | a = 0; | |
210 | ampa = 0; | |
211 | for (i = 0; i < 2 * sample_rate && k < nb_samples; i++, k++) { | |
212 | for (ch = 0; ch < channels; ch++) { | |
213 | double amp = (1.0 + sin(ampa)) * 0.15; | |
214 | if (ch & 1) | |
215 | amp = 0.30 - amp; | |
216 | v = sin(a) * amp; | |
217 | PUT_SAMPLE | |
218 | a += M_PI * 500.0 * 2.0 / sample_rate; | |
219 | ampa += M_PI * 2.0 / sample_rate; | |
220 | } | |
221 | } | |
222 | } | |
223 | ||
224 | int main(int argc, char **argv){ | |
225 | int in_sample_rate, out_sample_rate, ch ,i, flush_count; | |
226 | uint64_t in_ch_layout, out_ch_layout; | |
227 | enum AVSampleFormat in_sample_fmt, out_sample_fmt; | |
228 | uint8_t array_in[SAMPLES*8*8]; | |
229 | uint8_t array_mid[SAMPLES*8*8*3]; | |
230 | uint8_t array_out[SAMPLES*8*8+100]; | |
231 | uint8_t *ain[SWR_CH_MAX]; | |
232 | uint8_t *aout[SWR_CH_MAX]; | |
233 | uint8_t *amid[SWR_CH_MAX]; | |
234 | int flush_i=0; | |
235 | int mode; | |
236 | int num_tests = 10000; | |
237 | uint32_t seed = 0; | |
238 | uint32_t rand_seed = 0; | |
239 | int remaining_tests[FF_ARRAY_ELEMS(rates) * FF_ARRAY_ELEMS(layouts) * FF_ARRAY_ELEMS(formats) * FF_ARRAY_ELEMS(layouts) * FF_ARRAY_ELEMS(formats)]; | |
240 | int max_tests = FF_ARRAY_ELEMS(remaining_tests); | |
241 | int test; | |
242 | int specific_test= -1; | |
243 | ||
244 | struct SwrContext * forw_ctx= NULL; | |
245 | struct SwrContext *backw_ctx= NULL; | |
246 | ||
247 | if (argc > 1) { | |
248 | if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { | |
249 | av_log(NULL, AV_LOG_INFO, "Usage: swresample-test [<num_tests>[ <test>]] \n" | |
250 | "num_tests Default is %d\n", num_tests); | |
251 | return 0; | |
252 | } | |
253 | num_tests = strtol(argv[1], NULL, 0); | |
254 | if(num_tests < 0) { | |
255 | num_tests = -num_tests; | |
256 | rand_seed = time(0); | |
257 | } | |
258 | if(num_tests<= 0 || num_tests>max_tests) | |
259 | num_tests = max_tests; | |
260 | if(argc > 2) { | |
261 | specific_test = strtol(argv[1], NULL, 0); | |
262 | } | |
263 | } | |
264 | ||
265 | for(i=0; i<max_tests; i++) | |
266 | remaining_tests[i] = i; | |
267 | ||
268 | for(test=0; test<num_tests; test++){ | |
269 | unsigned r; | |
270 | uint_rand(seed); | |
271 | r = (seed * (uint64_t)(max_tests - test)) >>32; | |
272 | FFSWAP(int, remaining_tests[r], remaining_tests[max_tests - test - 1]); | |
273 | } | |
274 | qsort(remaining_tests + max_tests - num_tests, num_tests, sizeof(remaining_tests[0]), (void*)cmp); | |
275 | in_sample_rate=16000; | |
276 | for(test=0; test<num_tests; test++){ | |
277 | char in_layout_string[256]; | |
278 | char out_layout_string[256]; | |
279 | unsigned vector= remaining_tests[max_tests - test - 1]; | |
280 | int in_ch_count; | |
281 | int out_count, mid_count, out_ch_count; | |
282 | ||
283 | in_ch_layout = layouts[vector % FF_ARRAY_ELEMS(layouts)]; vector /= FF_ARRAY_ELEMS(layouts); | |
284 | out_ch_layout = layouts[vector % FF_ARRAY_ELEMS(layouts)]; vector /= FF_ARRAY_ELEMS(layouts); | |
285 | in_sample_fmt = formats[vector % FF_ARRAY_ELEMS(formats)]; vector /= FF_ARRAY_ELEMS(formats); | |
286 | out_sample_fmt = formats[vector % FF_ARRAY_ELEMS(formats)]; vector /= FF_ARRAY_ELEMS(formats); | |
287 | out_sample_rate = rates [vector % FF_ARRAY_ELEMS(rates )]; vector /= FF_ARRAY_ELEMS(rates); | |
288 | av_assert0(!vector); | |
289 | ||
290 | if(specific_test == 0){ | |
291 | if(out_sample_rate != in_sample_rate || in_ch_layout != out_ch_layout) | |
292 | continue; | |
293 | } | |
294 | ||
295 | in_ch_count= av_get_channel_layout_nb_channels(in_ch_layout); | |
296 | out_ch_count= av_get_channel_layout_nb_channels(out_ch_layout); | |
297 | av_get_channel_layout_string( in_layout_string, sizeof( in_layout_string), in_ch_count, in_ch_layout); | |
298 | av_get_channel_layout_string(out_layout_string, sizeof(out_layout_string), out_ch_count, out_ch_layout); | |
299 | fprintf(stderr, "TEST: %s->%s, rate:%5d->%5d, fmt:%s->%s\n", | |
300 | in_layout_string, out_layout_string, | |
301 | in_sample_rate, out_sample_rate, | |
302 | av_get_sample_fmt_name(in_sample_fmt), av_get_sample_fmt_name(out_sample_fmt)); | |
303 | forw_ctx = swr_alloc_set_opts(forw_ctx, out_ch_layout, out_sample_fmt, out_sample_rate, | |
304 | in_ch_layout, in_sample_fmt, in_sample_rate, | |
305 | 0, 0); | |
306 | backw_ctx = swr_alloc_set_opts(backw_ctx, in_ch_layout, in_sample_fmt, in_sample_rate, | |
307 | out_ch_layout, out_sample_fmt, out_sample_rate, | |
308 | 0, 0); | |
309 | if(!forw_ctx) { | |
310 | fprintf(stderr, "Failed to init forw_cts\n"); | |
311 | return 1; | |
312 | } | |
313 | if(!backw_ctx) { | |
314 | fprintf(stderr, "Failed to init backw_ctx\n"); | |
315 | return 1; | |
316 | } | |
317 | if(swr_init( forw_ctx) < 0) | |
318 | fprintf(stderr, "swr_init(->) failed\n"); | |
319 | if(swr_init(backw_ctx) < 0) | |
320 | fprintf(stderr, "swr_init(<-) failed\n"); | |
321 | //FIXME test planar | |
322 | setup_array(ain , array_in , in_sample_fmt, SAMPLES); | |
323 | setup_array(amid, array_mid, out_sample_fmt, 3*SAMPLES); | |
324 | setup_array(aout, array_out, in_sample_fmt , SAMPLES); | |
325 | #if 0 | |
326 | for(ch=0; ch<in_ch_count; ch++){ | |
327 | for(i=0; i<SAMPLES; i++) | |
328 | set(ain, ch, i, in_ch_count, in_sample_fmt, sin(i*i*3/SAMPLES)); | |
329 | } | |
330 | #else | |
331 | audiogen(ain, in_sample_fmt, in_ch_count, SAMPLES/6+1, SAMPLES); | |
332 | #endif | |
333 | mode = uint_rand(rand_seed) % 3; | |
334 | if(mode==0 /*|| out_sample_rate == in_sample_rate*/) { | |
335 | mid_count= swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, SAMPLES); | |
336 | } else if(mode==1){ | |
337 | mid_count= swr_convert(forw_ctx, amid, 0, (const uint8_t **)ain, SAMPLES); | |
338 | mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); | |
339 | } else { | |
340 | int tmp_count; | |
341 | mid_count= swr_convert(forw_ctx, amid, 0, (const uint8_t **)ain, 1); | |
342 | av_assert0(mid_count==0); | |
343 | shift(ain, 1, in_ch_count, in_sample_fmt); | |
344 | mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); | |
345 | shift(amid, mid_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; | |
346 | mid_count+=swr_convert(forw_ctx, amid, 2, (const uint8_t **)ain, 2); | |
347 | shift(amid, mid_count-tmp_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; | |
348 | shift(ain, 2, in_ch_count, in_sample_fmt); | |
349 | mid_count+=swr_convert(forw_ctx, amid, 1, (const uint8_t **)ain, SAMPLES-3); | |
350 | shift(amid, mid_count-tmp_count, out_ch_count, out_sample_fmt); tmp_count = mid_count; | |
351 | shift(ain, -3, in_ch_count, in_sample_fmt); | |
352 | mid_count+=swr_convert(forw_ctx, amid, 3*SAMPLES, (const uint8_t **)ain, 0); | |
353 | shift(amid, -tmp_count, out_ch_count, out_sample_fmt); | |
354 | } | |
355 | out_count= swr_convert(backw_ctx,aout, SAMPLES, (const uint8_t **)amid, mid_count); | |
356 | ||
357 | for(ch=0; ch<in_ch_count; ch++){ | |
358 | double sse, maxdiff=0; | |
359 | double sum_a= 0; | |
360 | double sum_b= 0; | |
361 | double sum_aa= 0; | |
362 | double sum_bb= 0; | |
363 | double sum_ab= 0; | |
364 | for(i=0; i<out_count; i++){ | |
365 | double a= get(ain , ch, i, in_ch_count, in_sample_fmt); | |
366 | double b= get(aout, ch, i, in_ch_count, in_sample_fmt); | |
367 | sum_a += a; | |
368 | sum_b += b; | |
369 | sum_aa+= a*a; | |
370 | sum_bb+= b*b; | |
371 | sum_ab+= a*b; | |
372 | maxdiff= FFMAX(maxdiff, FFABS(a-b)); | |
373 | } | |
374 | sse= sum_aa + sum_bb - 2*sum_ab; | |
375 | if(sse < 0 && sse > -0.00001) sse=0; //fix rounding error | |
376 | ||
377 | fprintf(stderr, "[e:%f c:%f max:%f] len:%5d\n", out_count ? sqrt(sse/out_count) : 0, sum_ab/(sqrt(sum_aa*sum_bb)), maxdiff, out_count); | |
378 | } | |
379 | ||
380 | flush_i++; | |
381 | flush_i%=21; | |
382 | flush_count = swr_convert(backw_ctx,aout, flush_i, 0, 0); | |
383 | shift(aout, flush_i, in_ch_count, in_sample_fmt); | |
384 | flush_count+= swr_convert(backw_ctx,aout, SAMPLES-flush_i, 0, 0); | |
385 | shift(aout, -flush_i, in_ch_count, in_sample_fmt); | |
386 | if(flush_count){ | |
387 | for(ch=0; ch<in_ch_count; ch++){ | |
388 | double sse, maxdiff=0; | |
389 | double sum_a= 0; | |
390 | double sum_b= 0; | |
391 | double sum_aa= 0; | |
392 | double sum_bb= 0; | |
393 | double sum_ab= 0; | |
394 | for(i=0; i<flush_count; i++){ | |
395 | double a= get(ain , ch, i+out_count, in_ch_count, in_sample_fmt); | |
396 | double b= get(aout, ch, i, in_ch_count, in_sample_fmt); | |
397 | sum_a += a; | |
398 | sum_b += b; | |
399 | sum_aa+= a*a; | |
400 | sum_bb+= b*b; | |
401 | sum_ab+= a*b; | |
402 | maxdiff= FFMAX(maxdiff, FFABS(a-b)); | |
403 | } | |
404 | sse= sum_aa + sum_bb - 2*sum_ab; | |
405 | if(sse < 0 && sse > -0.00001) sse=0; //fix rounding error | |
406 | ||
407 | fprintf(stderr, "[e:%f c:%f max:%f] len:%5d F:%3d\n", sqrt(sse/flush_count), sum_ab/(sqrt(sum_aa*sum_bb)), maxdiff, flush_count, flush_i); | |
408 | } | |
409 | } | |
410 | ||
411 | ||
412 | fprintf(stderr, "\n"); | |
413 | } | |
414 | ||
415 | return 0; | |
416 | } |