2 ** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
3 ** All rights reserved.
5 ** This code is released under 2-clause BSD license. Please see the
6 ** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
17 #if (HAVE_FFTW3 && HAVE_SNDFILE && HAVE_SYS_TIMES_H)
20 #include <sys/times.h>
24 #include <sys/utsname.h>
29 #define BUFFER_LEN 80000
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 ; \
40 double freqs
[MAX_FREQS
] ;
42 int output_samplerate
;
49 { const char *progname
;
50 const char *version_cmd
;
51 const char *version_start
;
52 const char *convert_cmd
;
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) ;
62 static char version_string
[512] ;
65 main (int argc
, char *argv
[])
66 { static RESAMPLE_PROG resample_progs
[] =
67 { { "sndfile-resample",
68 "examples/sndfile-resample --version",
70 "examples/sndfile-resample --max-speed -c 0 -to %d source.wav destination.wav",
71 SF_FORMAT_WAV
| SF_FORMAT_PCM_32
76 "sox source.wav -r %d destination.wav resample 0.835",
77 SF_FORMAT_WAV
| SF_FORMAT_PCM_32
80 "ResampAudio --version",
82 "ResampAudio -f cutoff=0.41,atten=100,ratio=128 -s %d source.wav destination.wav",
83 SF_FORMAT_WAV
| SF_FORMAT_PCM_32
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.
95 "ssrc --rate %d source.wav destination.wav",
96 SF_FORMAT_WAV | SF_FORMAT_PCM_32
101 ** The resample program is not able to match the bandwidth and SNR
102 ** specs or sndfile-resample and hence will not be tested.
107 "resample -to %d source.wav destination.wav",
108 SF_FORMAT_WAV | SF_FORMAT_FLOAT
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
119 } ; /* resample_progs */
122 int prog
= 0, verbose
= 0 ;
124 progname
= get_progname (argv
[0]) ;
126 printf ("\n %s : evaluate a sample rate converter.\n", progname
) ;
128 if (argc
== 3 && strcmp ("--verbose", argv
[1]) == 0)
130 prog
= atoi (argv
[2]) ;
134 prog
= atoi (argv
[1]) ;
137 usage_exit (progname
, resample_progs
, ARRAY_LEN (resample_progs
)) ;
139 if (prog
< 0 || prog
>= ARRAY_LEN (resample_progs
))
140 usage_exit (progname
, resample_progs
, ARRAY_LEN (resample_progs
)) ;
142 measure_program (& (resample_progs
[prog
]), verbose
) ;
149 /*==============================================================================
153 get_progname (char *progname
)
156 if ((cptr
= strrchr (progname
, '/')) != NULL
)
157 progname
= cptr
+ 1 ;
159 if ((cptr
= strrchr (progname
, '\\')) != NULL
)
160 progname
= cptr
+ 1 ;
166 usage_exit (const char *progname
, const RESAMPLE_PROG
*prog
, int count
)
169 printf ("\n Usage : %s <number>\n\n", progname
) ;
171 puts (" where <number> specifies the program to test:\n") ;
173 for (k
= 0 ; k
< count
; k
++)
174 printf (" %d : %s\n", k
, prog
[k
].progname
) ;
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") ;
185 get_machine_details (void)
186 { static char namestr
[256] ;
188 struct utsname name
;
190 if (uname (&name
) != 0)
191 { snprintf (namestr
, sizeof (namestr
), "Unknown") ;
195 snprintf (namestr
, sizeof (namestr
), "%s (%s %s %s)", name
.nodename
,
196 name
.machine
, name
.sysname
, name
.release
) ;
199 } /* get_machine_details */
202 /*==============================================================================
206 get_version_string (const RESAMPLE_PROG
*prog
)
211 snprintf (version_string
, sizeof (version_string
), "no version") ;
213 if (prog
->version_cmd
== NULL
)
216 if ((file
= popen (prog
->version_cmd
, "r")) == NULL
)
219 while ((cptr
= fgets (version_string
, sizeof (version_string
), file
)) != NULL
)
221 if (strstr (cptr
, prog
->version_start
) != NULL
)
224 version_string
[0] = 0 ;
229 /* Remove trailing newline. */
230 if ((cptr
= strchr (version_string
, '\n')) != NULL
)
233 /* Remove leading whitespace from version string. */
234 cptr
= version_string
;
235 while (cptr
[0] != 0 && isspace (cptr
[0]))
238 if (cptr
!= version_string
)
239 strncpy (version_string
, cptr
, sizeof (version_string
)) ;
242 } /* get_version_string */
245 generate_source_wav (const char *filename
, const double *freqs
, int freq_count
, int format
)
246 { static float buffer
[BUFFER_LEN
] ;
251 sfinfo
.channels
= 1 ;
252 sfinfo
.samplerate
= 44100 ;
253 sfinfo
.format
= format
;
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
)) ;
260 sf_command (sndfile
, SFC_SET_ADD_PEAK_CHUNK
, NULL
, SF_FALSE
) ;
262 gen_windowed_sines (freq_count
, freqs
, 0.9, buffer
, ARRAY_LEN (buffer
)) ;
264 if (sf_write_float (sndfile
, buffer
, ARRAY_LEN (buffer
)) != ARRAY_LEN (buffer
))
265 { printf ("Line %d : sf_write_float short write.\n", __LINE__
) ;
270 } /* generate_source_wav */
273 measure_destination_wav (char *filename
, int *output_samples
, int expected_peaks
)
274 { static float buffer
[250000] ;
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
)) ;
285 if (sfinfo
.channels
!= 1)
286 { printf ("Line %d : Bad channel count (%d). Should be 1.\n", __LINE__
, sfinfo
.channels
) ;
290 if (sfinfo
.frames
> ARRAY_LEN (buffer
))
291 { printf ("Line %d : Too many frames (%ld) of data in file.\n", __LINE__
, (long) sfinfo
.frames
) ;
295 *output_samples
= (int) sfinfo
.frames
;
297 if (sf_read_float (sndfile
, buffer
, sfinfo
.frames
) != sfinfo
.frames
)
298 { printf ("Line %d : Bad read.\n", __LINE__
) ;
304 snr
= calculate_snr (buffer
, sfinfo
.frames
, expected_peaks
) ;
307 } /* measure_desination_wav */
310 measure_snr (const RESAMPLE_PROG
*prog
, int *output_samples
, int verbose
)
311 { static SNR_TEST snr_test
[] =
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 }
325 static char command
[256] ;
327 double snr
, worst_snr
= 500.0 ;
328 int k
, retval
, sample_count
;
330 *output_samples
= 0 ;
332 for (k
= 0 ; k
< ARRAY_LEN (snr_test
) ; k
++)
333 { remove ("source.wav") ;
334 remove ("destination.wav") ;
337 printf (" SNR test #%d : ", k
) ;
339 generate_source_wav ("source.wav", snr_test
[k
].freqs
, snr_test
[k
].freq_count
, prog
->format
) ;
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
) ;
346 snr
= measure_destination_wav ("destination.wav", &sample_count
, snr_test
->pass_band_peaks
) ;
348 *output_samples
+= sample_count
;
350 if (fabs (snr
) < fabs (worst_snr
))
351 worst_snr
= fabs (snr
) ;
354 printf ("%6.2f dB\n", snr
) ;
360 /*------------------------------------------------------------------------------
364 measure_destination_peak (const char *filename
)
365 { static float data
[2 * BUFFER_LEN
] ;
371 if ((sndfile
= sf_open (filename
, SFM_READ
, &sfinfo
)) == NULL
)
372 { printf ("Line %d : failed to open file %s\n", __LINE__
, filename
) ;
376 if (sfinfo
.channels
!= 1)
377 { printf ("Line %d : bad channel count.\n", __LINE__
) ;
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
)) ;
386 if (sf_read_float (sndfile
, data
, sfinfo
.frames
) != sfinfo
.frames
)
387 { printf ("Line %d : bad read.\n", __LINE__
) ;
393 for (k
= 0 ; k
< (int) sfinfo
.frames
; k
++)
394 if (fabs (data
[k
]) > peak
)
395 peak
= fabs (data
[k
]) ;
398 } /* measure_destination_peak */
401 find_attenuation (double freq
, const RESAMPLE_PROG
*prog
, int verbose
)
402 { static char command
[256] ;
407 filename
= "destination.wav" ;
409 generate_source_wav ("source.wav", &freq
, 1, prog
->format
) ;
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
) ;
418 output_peak
= measure_destination_peak (filename
) ;
421 printf (" freq : %f peak : %f\n", freq
, output_peak
) ;
423 return fabs (20.0 * log10 (output_peak
)) ;
424 } /* find_attenuation */
427 bandwidth_test (const RESAMPLE_PROG
*prog
, int verbose
)
428 { double f1
, f2
, a1
, a2
;
432 a1
= find_attenuation (f1
, prog
, verbose
) ;
435 a2
= find_attenuation (f2
, prog
, verbose
) ;
438 if (fabs (a1
) < 1e-2 && a2
< 3.0)
441 if (a1
> 3.0 || a2
< 3.0)
442 { printf ("\n\nLine %d : cannot bracket 3dB point.\n\n", __LINE__
) ;
446 while (a2
- a1
> 1.0)
447 { freq
= f1
+ 0.5 * (f2
- f1
) ;
448 atten
= find_attenuation (freq
, prog
, verbose
) ;
460 freq
= f1
+ (3.0 - a1
) * (f2
- f1
) / (a2
- a1
) ;
462 return 200.0 * freq
;
463 } /* bandwidth_test */
466 measure_program (const RESAMPLE_PROG
*prog
, int verbose
)
467 { double snr
, bandwidth
, conversion_rate
;
469 struct tms time_data
;
472 printf ("\n Machine : %s\n", get_machine_details ()) ;
473 time_now
= time (NULL
) ;
474 printf (" Date : %s", ctime (&time_now
)) ;
476 get_version_string (prog
) ;
477 printf (" Program : %s\n", version_string
) ;
478 printf (" Command : %s\n\n", prog
->convert_cmd
) ;
480 snr
= measure_snr (prog
, &output_samples
, verbose
) ;
482 printf (" Worst case SNR : %6.2f dB\n", snr
) ;
486 conversion_rate
= (1.0 * output_samples
* sysconf (_SC_CLK_TCK
)) / time_data
.tms_cutime
;
488 printf (" Conversion rate : %5.0f samples/sec\n", conversion_rate
) ;
490 bandwidth
= bandwidth_test (prog
, verbose
) ;
493 printf (" Measured bandwidth : %5.2f %%\n", bandwidth
) ;
495 printf (" Could not measure bandwidth (no -3dB point found).\n") ;
498 } /* measure_program */
500 /*##############################################################################
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") ;
518 #endif /* (HAVE_FFTW3 && HAVE_SNDFILE) */