Bundle libsamplerate
[audio-libsamplerate.git] / libsamplerate / tests / snr_bw_test.c
diff --git a/libsamplerate/tests/snr_bw_test.c b/libsamplerate/tests/snr_bw_test.c
new file mode 100644 (file)
index 0000000..5c69f7f
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
+** All rights reserved.
+**
+** This code is released under 2-clause BSD license. Please see the
+** file at : https://github.com/erikd/libsamplerate/blob/master/COPYING
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+
+#if (HAVE_FFTW3)
+
+#include <fftw3.h>
+
+#include <samplerate.h>
+
+#include "util.h"
+
+#define        BUFFER_LEN              50000
+#define        MAX_FREQS               4
+#define        MAX_RATIOS              6
+#define        MAX_SPEC_LEN    (1<<15)
+
+#ifndef        M_PI
+#define        M_PI                    3.14159265358979323846264338
+#endif
+
+enum
+{      BOOLEAN_FALSE   = 0,
+       BOOLEAN_TRUE    = 1
+} ;
+
+typedef struct
+{      int             freq_count ;
+       double  freqs [MAX_FREQS] ;
+
+       double  src_ratio ;
+       int             pass_band_peaks ;
+
+       double  snr ;
+       double  peak_value ;
+} SINGLE_TEST ;
+
+typedef struct
+{      int                     converter ;
+       int                     tests ;
+       int                     do_bandwidth_test ;
+       SINGLE_TEST     test_data [10] ;
+} CONVERTER_TEST ;
+
+static double snr_test (SINGLE_TEST *snr_test_data, int number, int converter, int verbose) ;
+static double find_peak (float *output, int output_len) ;
+static double bandwidth_test (int converter, int verbose) ;
+
+int
+main (int argc, char *argv [])
+{      CONVERTER_TEST snr_test_data [] =
+       {
+               {       SRC_ZERO_ORDER_HOLD,
+                       8,
+                       BOOLEAN_FALSE,
+                       {       {       1,      { 0.01111111111 },              3.0,            1,       28.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.6,            1,       36.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.3,            1,       36.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.0,            1,      150.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.001,          1,       38.0,  1.0 },
+                               {       2,      { 0.011111, 0.324 },    1.9999,         2,       14.0,  1.0 },
+                               {       2,      { 0.012345, 0.457 },    0.456789,       1,       12.0,  1.0 },
+                               {       1,      { 0.3511111111 },               1.33,           1,       10.0,  1.0 }
+                               }
+                       },
+
+               {       SRC_LINEAR,
+                       8,
+                       BOOLEAN_FALSE,
+                       {       {       1,      { 0.01111111111 },              3.0,            1,       73.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.6,            1,       73.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.3,            1,       73.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.0,            1,      150.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.001,          1,       77.0,  1.0 },
+                               {       2,      { 0.011111, 0.324 },    1.9999,         2,       15.0,  0.94 },
+                               {       2,      { 0.012345, 0.457 },    0.456789,       1,       25.0,  0.96 },
+                               {       1,      { 0.3511111111 },               1.33,           1,       22.0,  0.99 }
+                               }
+                       },
+
+               {       SRC_SINC_FASTEST,
+                       9,
+                       BOOLEAN_TRUE,
+                       {       {       1,      { 0.01111111111 },              3.0,            1,      100.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.6,            1,       99.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.3,            1,      100.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.0,            1,      150.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.001,          1,      100.0,  1.0 },
+                               {       2,      { 0.011111, 0.324 },    1.9999,         2,       97.0,  1.0 },
+                               {       2,      { 0.012345, 0.457 },    0.456789,       1,      100.0,  0.5 },
+                               {       2,      { 0.011111, 0.45 },             0.6,            1,       97.0,  0.5 },
+                               {       1,      { 0.3511111111 },               1.33,           1,       97.0,  1.0 }
+                               }
+                       },
+
+               {       SRC_SINC_MEDIUM_QUALITY,
+                       9,
+                       BOOLEAN_TRUE,
+                       {       {       1,      { 0.01111111111 },              3.0,            1,      145.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.6,            1,      132.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.3,            1,      138.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.0,            1,      157.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.001,          1,      148.0,  1.0 },
+                               {       2,      { 0.011111, 0.324 },    1.9999,         2,      127.0,  1.0 },
+                               {       2,      { 0.012345, 0.457 },    0.456789,       1,      123.0,  0.5 },
+                               {       2,      { 0.011111, 0.45 },             0.6,            1,      126.0,  0.5 },
+                               {       1,      { 0.43111111111 },              1.33,           1,      121.0,  1.0 }
+                               }
+                       },
+
+               {       SRC_SINC_BEST_QUALITY,
+                       9,
+                       BOOLEAN_TRUE,
+                       {       {       1,      { 0.01111111111 },              3.0,            1,      147.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.6,            1,      147.0,  1.0 },
+                               {       1,      { 0.01111111111 },              0.3,            1,      148.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.0,            1,      155.0,  1.0 },
+                               {       1,      { 0.01111111111 },              1.001,          1,      148.0,  1.0 },
+                               {       2,      { 0.011111, 0.324 },    1.9999,         2,      146.0,  1.0 },
+                               {       2,      { 0.012345, 0.457 },    0.456789,       1,      147.0,  0.5 },
+                               {       2,      { 0.011111, 0.45 },             0.6,            1,      144.0,  0.5 },
+                               {       1,      { 0.43111111111 },              1.33,           1,      145.0,  1.0 }
+                               }
+                       },
+               } ; /* snr_test_data */
+
+       double  best_snr, snr, freq3dB ;
+       int     j, k, converter, verbose = 0 ;
+
+       if (argc == 2 && strcmp (argv [1], "--verbose") == 0)
+               verbose = 1 ;
+
+       puts ("") ;
+
+       for (j = 0 ; j < ARRAY_LEN (snr_test_data) ; j++)
+       {       best_snr = 5000.0 ;
+
+               converter = snr_test_data [j].converter ;
+
+               printf ("    Converter %d : %s\n", converter, src_get_name (converter)) ;
+               printf ("    %s\n", src_get_description (converter)) ;
+
+               for (k = 0 ; k < snr_test_data [j].tests ; k++)
+               {       snr = snr_test (&(snr_test_data [j].test_data [k]), k, converter, verbose) ;
+                       if (best_snr > snr)
+                               best_snr = snr ;
+                       } ;
+
+               printf ("    Worst case Signal-to-Noise Ratio : %.2f dB.\n", best_snr) ;
+
+               if (snr_test_data [j].do_bandwidth_test == BOOLEAN_FALSE)
+               {       puts ("    Bandwith test not performed on this converter.\n") ;
+                       continue ;
+                       }
+
+               freq3dB = bandwidth_test (converter, verbose) ;
+
+               printf ("    Measured -3dB rolloff point      : %5.2f %%.\n\n", freq3dB) ;
+               } ;
+
+       fftw_cleanup () ;
+
+       return 0 ;
+} /* main */
+
+/*==============================================================================
+*/
+
+static double
+snr_test (SINGLE_TEST *test_data, int number, int converter, int verbose)
+{      static float data [BUFFER_LEN + 1] ;
+       static float output [MAX_SPEC_LEN] ;
+
+       SRC_STATE       *src_state ;
+       SRC_DATA        src_data ;
+
+       double          output_peak, snr ;
+       int             k, output_len, input_len, error ;
+
+       if (verbose != 0)
+       {       printf ("\tSignal-to-Noise Ratio Test %d.\n"
+                               "\t=====================================\n", number) ;
+               printf ("\tFrequencies : [ ") ;
+               for (k = 0 ; k < test_data->freq_count ; k++)
+                       printf ("%6.4f ", test_data->freqs [k]) ;
+
+               printf ("]\n\tSRC Ratio   : %8.4f\n", test_data->src_ratio) ;
+               }
+       else
+       {       printf ("\tSignal-to-Noise Ratio Test %d : ", number) ;
+               fflush (stdout) ;
+               } ;
+
+       /* Set up the output array. */
+       if (test_data->src_ratio >= 1.0)
+       {       output_len = MAX_SPEC_LEN ;
+               input_len = (int) ceil (MAX_SPEC_LEN / test_data->src_ratio) ;
+               if (input_len > BUFFER_LEN)
+                       input_len = BUFFER_LEN ;
+               }
+       else
+       {       input_len = BUFFER_LEN ;
+               output_len = (int) ceil (BUFFER_LEN * test_data->src_ratio) ;
+               output_len &= ((~0u) << 4) ;
+               if (output_len > MAX_SPEC_LEN)
+                       output_len = MAX_SPEC_LEN ;
+               input_len = (int) ceil (output_len / test_data->src_ratio) ;
+               } ;
+
+       memset (output, 0, sizeof (output)) ;
+
+       /* Generate input data array. */
+       gen_windowed_sines (test_data->freq_count, test_data->freqs, 1.0, data, input_len) ;
+
+       /* Perform sample rate conversion. */
+       if ((src_state = src_new (converter, 1, &error)) == NULL)
+       {       printf ("\n\nLine %d : src_new() failed : %s.\n\n", __LINE__, src_strerror (error)) ;
+               exit (1) ;
+               } ;
+
+       src_data.end_of_input = 1 ; /* Only one buffer worth of input. */
+
+       src_data.data_in = data ;
+       src_data.input_frames = input_len ;
+
+       src_data.src_ratio = test_data->src_ratio ;
+
+       src_data.data_out = output ;
+       src_data.output_frames = output_len ;
+
+       if ((error = src_process (src_state, &src_data)))
+       {       printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
+               exit (1) ;
+               } ;
+
+       src_state = src_delete (src_state) ;
+
+       if (verbose != 0)
+               printf ("\tOutput Len  :   %ld\n", src_data.output_frames_gen) ;
+
+       if (abs (src_data.output_frames_gen - output_len) > 4)
+       {       printf ("\n\nLine %d : output data length should be %d.\n\n", __LINE__, output_len) ;
+               exit (1) ;
+               } ;
+
+       /* Check output peak. */
+       output_peak = find_peak (output, src_data.output_frames_gen) ;
+
+       if (verbose != 0)
+               printf ("\tOutput Peak :   %6.4f\n", output_peak) ;
+
+       if (fabs (output_peak - test_data->peak_value) > 0.01)
+       {       printf ("\n\nLine %d : output peak (%6.4f) should be %6.4f\n\n", __LINE__, output_peak, test_data->peak_value) ;
+               save_oct_float ("snr_test.dat", data, BUFFER_LEN, output, output_len) ;
+               exit (1) ;
+               } ;
+
+       /* Calculate signal-to-noise ratio. */
+       snr = calculate_snr (output, src_data.output_frames_gen, test_data->pass_band_peaks) ;
+
+       if (snr < 0.0)
+       {       /* An error occurred. */
+               save_oct_float ("snr_test.dat", data, BUFFER_LEN, output, src_data.output_frames_gen) ;
+               exit (1) ;
+               } ;
+
+       if (verbose != 0)
+               printf ("\tSNR Ratio   :   %.2f dB\n", snr) ;
+
+       if (snr < test_data->snr)
+       {       printf ("\n\nLine %d : SNR (%5.2f) should be > %6.2f dB\n\n", __LINE__, snr, test_data->snr) ;
+               exit (1) ;
+               } ;
+
+       if (verbose != 0)
+               puts ("\t-------------------------------------\n\tPass\n") ;
+       else
+               puts ("Pass") ;
+
+       return snr ;
+} /* snr_test */
+
+static double
+find_peak (float *data, int len)
+{      double  peak = 0.0 ;
+       int             k = 0 ;
+
+       for (k = 0 ; k < len ; k++)
+               if (fabs (data [k]) > peak)
+                       peak = fabs (data [k]) ;
+
+       return peak ;
+} /* find_peak */
+
+
+static double
+find_attenuation (double freq, int converter, int verbose)
+{      static float input      [BUFFER_LEN] ;
+       static float output [2 * BUFFER_LEN] ;
+
+       SRC_DATA        src_data ;
+       double          output_peak ;
+       int                     error ;
+
+       gen_windowed_sines (1, &freq, 1.0, input, BUFFER_LEN) ;
+
+       src_data.end_of_input = 1 ; /* Only one buffer worth of input. */
+
+       src_data.data_in = input ;
+       src_data.input_frames = BUFFER_LEN ;
+
+       src_data.src_ratio = 1.999 ;
+
+       src_data.data_out = output ;
+       src_data.output_frames = ARRAY_LEN (output) ;
+
+       if ((error = src_simple (&src_data, converter, 1)))
+       {       printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ;
+               exit (1) ;
+               } ;
+
+       output_peak = find_peak (output, ARRAY_LEN (output)) ;
+
+       if (verbose)
+               printf ("\tFreq : %6f   InPeak : %6f    OutPeak : %6f   Atten : %6.2f dB\n",
+                               freq, 1.0, output_peak, 20.0 * log10 (1.0 / output_peak)) ;
+
+       return 20.0 * log10 (1.0 / output_peak) ;
+} /* find_attenuation */
+
+static double
+bandwidth_test (int converter, int verbose)
+{      double  f1, f2, a1, a2 ;
+       double  freq, atten ;
+
+       f1 = 0.35 ;
+       a1 = find_attenuation (f1, converter, verbose) ;
+
+       f2 = 0.495 ;
+       a2 = find_attenuation (f2, converter, verbose) ;
+
+       if (a1 > 3.0 || a2 < 3.0)
+       {       printf ("\n\nLine %d : cannot bracket 3dB point.\n\n", __LINE__) ;
+               exit (1) ;
+               } ;
+
+       while (a2 - a1 > 1.0)
+       {       freq = f1 + 0.5 * (f2 - f1) ;
+               atten = find_attenuation (freq, converter, verbose) ;
+
+               if (atten < 3.0)
+               {       f1 = freq ;
+                       a1 = atten ;
+                       }
+               else
+               {       f2 = freq ;
+                       a2 = atten ;
+                       } ;
+               } ;
+
+       freq = f1 + (3.0 - a1) * (f2 - f1) / (a2 - a1) ;
+
+       return 200.0 * freq ;
+} /* bandwidth_test */
+
+#else /* (HAVE_FFTW3) == 0 */
+
+/* Alternative main function when librfftw is not available. */
+
+int
+main (void)
+{      puts ("\n"
+               "****************************************************************\n"
+               " This test cannot be run without FFTW (http://www.fftw.org/).\n"
+               " Both the real and the complex versions of the library are\n"
+               " required.") ;
+       puts ("****************************************************************\n") ;
+
+       return 0 ;
+} /* main */
+
+#endif
+
This page took 0.015639 seconds and 4 git commands to generate.