Mercurial > hg > sv-dependency-builds
comparison src/libsamplerate-0.1.9/tests/src-evaluate.c @ 41:481f5f8c5634
Current libsamplerate source
author | Chris Cannam |
---|---|
date | Tue, 18 Oct 2016 13:24:45 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
40:1df64224f5ac | 41:481f5f8c5634 |
---|---|
1 /* | |
2 ** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com> | |
3 ** All rights reserved. | |
4 ** | |
5 ** This code is released under 2-clause BSD license. Please see the | |
6 ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING | |
7 */ | |
8 | |
9 #include <stdio.h> | |
10 #include <stdlib.h> | |
11 #include <unistd.h> | |
12 #include <string.h> | |
13 #include <ctype.h> | |
14 | |
15 #include "config.h" | |
16 | |
17 #if (HAVE_FFTW3 && HAVE_SNDFILE && HAVE_SYS_TIMES_H) | |
18 | |
19 #include <time.h> | |
20 #include <sys/times.h> | |
21 | |
22 #include <sndfile.h> | |
23 #include <math.h> | |
24 #include <sys/utsname.h> | |
25 | |
26 #include "util.h" | |
27 | |
28 #define MAX_FREQS 4 | |
29 #define BUFFER_LEN 80000 | |
30 | |
31 #define SAFE_STRNCAT(dest,src,len) \ | |
32 { int safe_strncat_count ; \ | |
33 safe_strncat_count = (len) - strlen (dest) - 1 ; \ | |
34 strncat ((dest), (src), safe_strncat_count) ; \ | |
35 (dest) [(len) - 1] = 0 ; \ | |
36 } ; | |
37 | |
38 typedef struct | |
39 { int freq_count ; | |
40 double freqs [MAX_FREQS] ; | |
41 | |
42 int output_samplerate ; | |
43 int pass_band_peaks ; | |
44 | |
45 double peak_value ; | |
46 } SNR_TEST ; | |
47 | |
48 typedef struct | |
49 { const char *progname ; | |
50 const char *version_cmd ; | |
51 const char *version_start ; | |
52 const char *convert_cmd ; | |
53 int format ; | |
54 } RESAMPLE_PROG ; | |
55 | |
56 static char *get_progname (char *) ; | |
57 static void usage_exit (const char *, const RESAMPLE_PROG *prog, int count) ; | |
58 static void measure_program (const RESAMPLE_PROG *prog, int verbose) ; | |
59 static void generate_source_wav (const char *filename, const double *freqs, int freq_count, int format) ; | |
60 static const char* get_machine_details (void) ; | |
61 | |
62 static char version_string [512] ; | |
63 | |
64 int | |
65 main (int argc, char *argv []) | |
66 { static RESAMPLE_PROG resample_progs [] = | |
67 { { "sndfile-resample", | |
68 "examples/sndfile-resample --version", | |
69 "libsamplerate", | |
70 "examples/sndfile-resample --max-speed -c 0 -to %d source.wav destination.wav", | |
71 SF_FORMAT_WAV | SF_FORMAT_PCM_32 | |
72 }, | |
73 { "sox", | |
74 "sox -h 2>&1", | |
75 "sox", | |
76 "sox source.wav -r %d destination.wav resample 0.835", | |
77 SF_FORMAT_WAV | SF_FORMAT_PCM_32 | |
78 }, | |
79 { "ResampAudio", | |
80 "ResampAudio --version", | |
81 "ResampAudio", | |
82 "ResampAudio -f cutoff=0.41,atten=100,ratio=128 -s %d source.wav destination.wav", | |
83 SF_FORMAT_WAV | SF_FORMAT_PCM_32 | |
84 }, | |
85 | |
86 /*- | |
87 { /+* | |
88 ** The Shibatch converter doesn't work for all combinations of | |
89 ** source and destination sample rates. Therefore it can't be | |
90 ** included in this test. | |
91 *+/ | |
92 "shibatch", | |
93 "ssrc", | |
94 "Shibatch", | |
95 "ssrc --rate %d source.wav destination.wav", | |
96 SF_FORMAT_WAV | SF_FORMAT_PCM_32 | |
97 },-*/ | |
98 | |
99 /*- | |
100 { /+* | |
101 ** The resample program is not able to match the bandwidth and SNR | |
102 ** specs or sndfile-resample and hence will not be tested. | |
103 *+/ | |
104 "resample", | |
105 "resample -version", | |
106 "resample", | |
107 "resample -to %d source.wav destination.wav", | |
108 SF_FORMAT_WAV | SF_FORMAT_FLOAT | |
109 },-*/ | |
110 | |
111 /*- | |
112 { "mplayer", | |
113 "mplayer -v 2>&1", | |
114 "MPlayer ", | |
115 "mplayer -ao pcm -srate %d source.wav >/dev/null 2>&1 && mv audiodump.wav destination.wav", | |
116 SF_FORMAT_WAV | SF_FORMAT_PCM_32 | |
117 },-*/ | |
118 | |
119 } ; /* resample_progs */ | |
120 | |
121 char *progname ; | |
122 int prog = 0, verbose = 0 ; | |
123 | |
124 progname = get_progname (argv [0]) ; | |
125 | |
126 printf ("\n %s : evaluate a sample rate converter.\n", progname) ; | |
127 | |
128 if (argc == 3 && strcmp ("--verbose", argv [1]) == 0) | |
129 { verbose = 1 ; | |
130 prog = atoi (argv [2]) ; | |
131 } | |
132 else if (argc == 2) | |
133 { verbose = 0 ; | |
134 prog = atoi (argv [1]) ; | |
135 } | |
136 else | |
137 usage_exit (progname, resample_progs, ARRAY_LEN (resample_progs)) ; | |
138 | |
139 if (prog < 0 || prog >= ARRAY_LEN (resample_progs)) | |
140 usage_exit (progname, resample_progs, ARRAY_LEN (resample_progs)) ; | |
141 | |
142 measure_program (& (resample_progs [prog]), verbose) ; | |
143 | |
144 puts ("") ; | |
145 | |
146 return 0 ; | |
147 } /* main */ | |
148 | |
149 /*============================================================================== | |
150 */ | |
151 | |
152 static char * | |
153 get_progname (char *progname) | |
154 { char *cptr ; | |
155 | |
156 if ((cptr = strrchr (progname, '/')) != NULL) | |
157 progname = cptr + 1 ; | |
158 | |
159 if ((cptr = strrchr (progname, '\\')) != NULL) | |
160 progname = cptr + 1 ; | |
161 | |
162 return progname ; | |
163 } /* get_progname */ | |
164 | |
165 static void | |
166 usage_exit (const char *progname, const RESAMPLE_PROG *prog, int count) | |
167 { int k ; | |
168 | |
169 printf ("\n Usage : %s <number>\n\n", progname) ; | |
170 | |
171 puts (" where <number> specifies the program to test:\n") ; | |
172 | |
173 for (k = 0 ; k < count ; k++) | |
174 printf (" %d : %s\n", k, prog [k].progname) ; | |
175 | |
176 puts ("\n" | |
177 " Obviously to test a given program you have to have it available on\n" | |
178 " your system. See http://www.mega-nerd.com/SRC/quality.html for\n" | |
179 " the download location of these programs.\n") ; | |
180 | |
181 exit (1) ; | |
182 } /* usage_exit */ | |
183 | |
184 static const char* | |
185 get_machine_details (void) | |
186 { static char namestr [256] ; | |
187 | |
188 struct utsname name ; | |
189 | |
190 if (uname (&name) != 0) | |
191 { snprintf (namestr, sizeof (namestr), "Unknown") ; | |
192 return namestr ; | |
193 } ; | |
194 | |
195 snprintf (namestr, sizeof (namestr), "%s (%s %s %s)", name.nodename, | |
196 name.machine, name.sysname, name.release) ; | |
197 | |
198 return namestr ; | |
199 } /* get_machine_details */ | |
200 | |
201 | |
202 /*============================================================================== | |
203 */ | |
204 | |
205 static void | |
206 get_version_string (const RESAMPLE_PROG *prog) | |
207 { FILE *file ; | |
208 char *cptr ; | |
209 | |
210 /* Default. */ | |
211 snprintf (version_string, sizeof (version_string), "no version") ; | |
212 | |
213 if (prog->version_cmd == NULL) | |
214 return ; | |
215 | |
216 if ((file = popen (prog->version_cmd, "r")) == NULL) | |
217 return ; | |
218 | |
219 while ((cptr = fgets (version_string, sizeof (version_string), file)) != NULL) | |
220 { | |
221 if (strstr (cptr, prog->version_start) != NULL) | |
222 break ; | |
223 | |
224 version_string [0] = 0 ; | |
225 } ; | |
226 | |
227 pclose (file) ; | |
228 | |
229 /* Remove trailing newline. */ | |
230 if ((cptr = strchr (version_string, '\n')) != NULL) | |
231 cptr [0] = 0 ; | |
232 | |
233 /* Remove leading whitespace from version string. */ | |
234 cptr = version_string ; | |
235 while (cptr [0] != 0 && isspace (cptr [0])) | |
236 cptr ++ ; | |
237 | |
238 if (cptr != version_string) | |
239 strncpy (version_string, cptr, sizeof (version_string)) ; | |
240 | |
241 return ; | |
242 } /* get_version_string */ | |
243 | |
244 static void | |
245 generate_source_wav (const char *filename, const double *freqs, int freq_count, int format) | |
246 { static float buffer [BUFFER_LEN] ; | |
247 | |
248 SNDFILE *sndfile ; | |
249 SF_INFO sfinfo ; | |
250 | |
251 sfinfo.channels = 1 ; | |
252 sfinfo.samplerate = 44100 ; | |
253 sfinfo.format = format ; | |
254 | |
255 if ((sndfile = sf_open (filename, SFM_WRITE, &sfinfo)) == NULL) | |
256 { printf ("Line %d : cound not open '%s' : %s\n", __LINE__, filename, sf_strerror (NULL)) ; | |
257 exit (1) ; | |
258 } ; | |
259 | |
260 sf_command (sndfile, SFC_SET_ADD_PEAK_CHUNK, NULL, SF_FALSE) ; | |
261 | |
262 gen_windowed_sines (freq_count, freqs, 0.9, buffer, ARRAY_LEN (buffer)) ; | |
263 | |
264 if (sf_write_float (sndfile, buffer, ARRAY_LEN (buffer)) != ARRAY_LEN (buffer)) | |
265 { printf ("Line %d : sf_write_float short write.\n", __LINE__) ; | |
266 exit (1) ; | |
267 } ; | |
268 | |
269 sf_close (sndfile) ; | |
270 } /* generate_source_wav */ | |
271 | |
272 static double | |
273 measure_destination_wav (char *filename, int *output_samples, int expected_peaks) | |
274 { static float buffer [250000] ; | |
275 | |
276 SNDFILE *sndfile ; | |
277 SF_INFO sfinfo ; | |
278 double snr ; | |
279 | |
280 if ((sndfile = sf_open (filename, SFM_READ, &sfinfo)) == NULL) | |
281 { printf ("Line %d : Cound not open '%s' : %s\n", __LINE__, filename, sf_strerror (NULL)) ; | |
282 exit (1) ; | |
283 } ; | |
284 | |
285 if (sfinfo.channels != 1) | |
286 { printf ("Line %d : Bad channel count (%d). Should be 1.\n", __LINE__, sfinfo.channels) ; | |
287 exit (1) ; | |
288 } ; | |
289 | |
290 if (sfinfo.frames > ARRAY_LEN (buffer)) | |
291 { printf ("Line %d : Too many frames (%ld) of data in file.\n", __LINE__, (long) sfinfo.frames) ; | |
292 exit (1) ; | |
293 } ; | |
294 | |
295 *output_samples = (int) sfinfo.frames ; | |
296 | |
297 if (sf_read_float (sndfile, buffer, sfinfo.frames) != sfinfo.frames) | |
298 { printf ("Line %d : Bad read.\n", __LINE__) ; | |
299 exit (1) ; | |
300 } ; | |
301 | |
302 sf_close (sndfile) ; | |
303 | |
304 snr = calculate_snr (buffer, sfinfo.frames, expected_peaks) ; | |
305 | |
306 return snr ; | |
307 } /* measure_desination_wav */ | |
308 | |
309 static double | |
310 measure_snr (const RESAMPLE_PROG *prog, int *output_samples, int verbose) | |
311 { static SNR_TEST snr_test [] = | |
312 { | |
313 { 1, { 0.211111111111 }, 48000, 1, 1.0 }, | |
314 { 1, { 0.011111111111 }, 132301, 1, 1.0 }, | |
315 { 1, { 0.111111111111 }, 92301, 1, 1.0 }, | |
316 { 1, { 0.011111111111 }, 26461, 1, 1.0 }, | |
317 { 1, { 0.011111111111 }, 13231, 1, 1.0 }, | |
318 { 1, { 0.011111111111 }, 44101, 1, 1.0 }, | |
319 { 2, { 0.311111, 0.49 }, 78199, 2, 1.0 }, | |
320 { 2, { 0.011111, 0.49 }, 12345, 1, 0.5 }, | |
321 { 2, { 0.0123456, 0.4 }, 20143, 1, 0.5 }, | |
322 { 2, { 0.0111111, 0.4 }, 26461, 1, 0.5 }, | |
323 { 1, { 0.381111111111 }, 58661, 1, 1.0 } | |
324 } ; /* snr_test */ | |
325 static char command [256] ; | |
326 | |
327 double snr, worst_snr = 500.0 ; | |
328 int k , retval, sample_count ; | |
329 | |
330 *output_samples = 0 ; | |
331 | |
332 for (k = 0 ; k < ARRAY_LEN (snr_test) ; k++) | |
333 { remove ("source.wav") ; | |
334 remove ("destination.wav") ; | |
335 | |
336 if (verbose) | |
337 printf (" SNR test #%d : ", k) ; | |
338 fflush (stdout) ; | |
339 generate_source_wav ("source.wav", snr_test [k].freqs, snr_test [k].freq_count, prog->format) ; | |
340 | |
341 snprintf (command, sizeof (command), prog->convert_cmd, snr_test [k].output_samplerate) ; | |
342 SAFE_STRNCAT (command, " >/dev/null 2>&1", sizeof (command)) ; | |
343 if ((retval = system (command)) != 0) | |
344 printf ("system returned %d\n", retval) ; | |
345 | |
346 snr = measure_destination_wav ("destination.wav", &sample_count, snr_test->pass_band_peaks) ; | |
347 | |
348 *output_samples += sample_count ; | |
349 | |
350 if (fabs (snr) < fabs (worst_snr)) | |
351 worst_snr = fabs (snr) ; | |
352 | |
353 if (verbose) | |
354 printf ("%6.2f dB\n", snr) ; | |
355 } ; | |
356 | |
357 return worst_snr ; | |
358 } /* measure_snr */ | |
359 | |
360 /*------------------------------------------------------------------------------ | |
361 */ | |
362 | |
363 static double | |
364 measure_destination_peak (const char *filename) | |
365 { static float data [2 * BUFFER_LEN] ; | |
366 SNDFILE *sndfile ; | |
367 SF_INFO sfinfo ; | |
368 double peak = 0.0 ; | |
369 int k = 0 ; | |
370 | |
371 if ((sndfile = sf_open (filename, SFM_READ, &sfinfo)) == NULL) | |
372 { printf ("Line %d : failed to open file %s\n", __LINE__, filename) ; | |
373 exit (1) ; | |
374 } ; | |
375 | |
376 if (sfinfo.channels != 1) | |
377 { printf ("Line %d : bad channel count.\n", __LINE__) ; | |
378 exit (1) ; | |
379 } ; | |
380 | |
381 if (sfinfo.frames > ARRAY_LEN (data) + 4 || sfinfo.frames < ARRAY_LEN (data) - 100) | |
382 { printf ("Line %d : bad frame count (got %d, expected %d).\n", __LINE__, (int) sfinfo.frames, ARRAY_LEN (data)) ; | |
383 exit (1) ; | |
384 } ; | |
385 | |
386 if (sf_read_float (sndfile, data, sfinfo.frames) != sfinfo.frames) | |
387 { printf ("Line %d : bad read.\n", __LINE__) ; | |
388 exit (1) ; | |
389 } ; | |
390 | |
391 sf_close (sndfile) ; | |
392 | |
393 for (k = 0 ; k < (int) sfinfo.frames ; k++) | |
394 if (fabs (data [k]) > peak) | |
395 peak = fabs (data [k]) ; | |
396 | |
397 return peak ; | |
398 } /* measure_destination_peak */ | |
399 | |
400 static double | |
401 find_attenuation (double freq, const RESAMPLE_PROG *prog, int verbose) | |
402 { static char command [256] ; | |
403 double output_peak ; | |
404 int retval ; | |
405 char *filename ; | |
406 | |
407 filename = "destination.wav" ; | |
408 | |
409 generate_source_wav ("source.wav", &freq, 1, prog->format) ; | |
410 | |
411 remove (filename) ; | |
412 | |
413 snprintf (command, sizeof (command), prog->convert_cmd, 88189) ; | |
414 SAFE_STRNCAT (command, " >/dev/null 2>&1", sizeof (command)) ; | |
415 if ((retval = system (command)) != 0) | |
416 printf ("system returned %d\n", retval) ; | |
417 | |
418 output_peak = measure_destination_peak (filename) ; | |
419 | |
420 if (verbose) | |
421 printf (" freq : %f peak : %f\n", freq, output_peak) ; | |
422 | |
423 return fabs (20.0 * log10 (output_peak)) ; | |
424 } /* find_attenuation */ | |
425 | |
426 static double | |
427 bandwidth_test (const RESAMPLE_PROG *prog, int verbose) | |
428 { double f1, f2, a1, a2 ; | |
429 double freq, atten ; | |
430 | |
431 f1 = 0.35 ; | |
432 a1 = find_attenuation (f1, prog, verbose) ; | |
433 | |
434 f2 = 0.49999 ; | |
435 a2 = find_attenuation (f2, prog, verbose) ; | |
436 | |
437 | |
438 if (fabs (a1) < 1e-2 && a2 < 3.0) | |
439 return -1.0 ; | |
440 | |
441 if (a1 > 3.0 || a2 < 3.0) | |
442 { printf ("\n\nLine %d : cannot bracket 3dB point.\n\n", __LINE__) ; | |
443 exit (1) ; | |
444 } ; | |
445 | |
446 while (a2 - a1 > 1.0) | |
447 { freq = f1 + 0.5 * (f2 - f1) ; | |
448 atten = find_attenuation (freq, prog, verbose) ; | |
449 | |
450 if (atten < 3.0) | |
451 { f1 = freq ; | |
452 a1 = atten ; | |
453 } | |
454 else | |
455 { f2 = freq ; | |
456 a2 = atten ; | |
457 } ; | |
458 } ; | |
459 | |
460 freq = f1 + (3.0 - a1) * (f2 - f1) / (a2 - a1) ; | |
461 | |
462 return 200.0 * freq ; | |
463 } /* bandwidth_test */ | |
464 | |
465 static void | |
466 measure_program (const RESAMPLE_PROG *prog, int verbose) | |
467 { double snr, bandwidth, conversion_rate ; | |
468 int output_samples ; | |
469 struct tms time_data ; | |
470 time_t time_now ; | |
471 | |
472 printf ("\n Machine : %s\n", get_machine_details ()) ; | |
473 time_now = time (NULL) ; | |
474 printf (" Date : %s", ctime (&time_now)) ; | |
475 | |
476 get_version_string (prog) ; | |
477 printf (" Program : %s\n", version_string) ; | |
478 printf (" Command : %s\n\n", prog->convert_cmd) ; | |
479 | |
480 snr = measure_snr (prog, &output_samples, verbose) ; | |
481 | |
482 printf (" Worst case SNR : %6.2f dB\n", snr) ; | |
483 | |
484 times (&time_data) ; | |
485 | |
486 conversion_rate = (1.0 * output_samples * sysconf (_SC_CLK_TCK)) / time_data.tms_cutime ; | |
487 | |
488 printf (" Conversion rate : %5.0f samples/sec\n", conversion_rate) ; | |
489 | |
490 bandwidth = bandwidth_test (prog, verbose) ; | |
491 | |
492 if (bandwidth > 0.0) | |
493 printf (" Measured bandwidth : %5.2f %%\n", bandwidth) ; | |
494 else | |
495 printf (" Could not measure bandwidth (no -3dB point found).\n") ; | |
496 | |
497 return ; | |
498 } /* measure_program */ | |
499 | |
500 /*############################################################################## | |
501 */ | |
502 | |
503 #else | |
504 | |
505 int | |
506 main (void) | |
507 { puts ("\n" | |
508 "****************************************************************\n" | |
509 " This program has been compiled without :\n" | |
510 " 1) FFTW (http://www.fftw.org/).\n" | |
511 " 2) libsndfile (http://www.zip.com.au/~erikd/libsndfile/).\n" | |
512 " Without these two libraries there is not much it can do.\n" | |
513 "****************************************************************\n") ; | |
514 | |
515 return 0 ; | |
516 } /* main */ | |
517 | |
518 #endif /* (HAVE_FFTW3 && HAVE_SNDFILE) */ | |
519 |