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