cannam@85: /* cannam@85: ** Copyright (C) 2002-2011 Erik de Castro Lopo cannam@85: ** cannam@85: ** This program is free software; you can redistribute it and/or modify cannam@85: ** it under the terms of the GNU General Public License as published by cannam@85: ** the Free Software Foundation; either version 2 of the License, or cannam@85: ** (at your option) any later version. cannam@85: ** cannam@85: ** This program is distributed in the hope that it will be useful, cannam@85: ** but WITHOUT ANY WARRANTY; without even the implied warranty of cannam@85: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the cannam@85: ** GNU General Public License for more details. cannam@85: ** cannam@85: ** You should have received a copy of the GNU General Public License cannam@85: ** along with this program; if not, write to the Free Software cannam@85: ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. cannam@85: */ cannam@85: cannam@85: #include "config.h" cannam@85: cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: #include cannam@85: cannam@85: #if (HAVE_FFTW3) cannam@85: cannam@85: #include cannam@85: cannam@85: #include "util.h" cannam@85: cannam@85: #define BUFFER_LEN 50000 cannam@85: #define MAX_FREQS 4 cannam@85: #define MAX_RATIOS 6 cannam@85: #define MAX_SPEC_LEN (1<<15) cannam@85: cannam@85: #ifndef M_PI cannam@85: #define M_PI 3.14159265358979323846264338 cannam@85: #endif cannam@85: cannam@85: enum cannam@85: { BOOLEAN_FALSE = 0, cannam@85: BOOLEAN_TRUE = 1 cannam@85: } ; cannam@85: cannam@85: typedef struct cannam@85: { int freq_count ; cannam@85: double freqs [MAX_FREQS] ; cannam@85: cannam@85: double src_ratio ; cannam@85: int pass_band_peaks ; cannam@85: cannam@85: double snr ; cannam@85: double peak_value ; cannam@85: } SINGLE_TEST ; cannam@85: cannam@85: typedef struct cannam@85: { int converter ; cannam@85: int tests ; cannam@85: int do_bandwidth_test ; cannam@85: SINGLE_TEST test_data [10] ; cannam@85: } CONVERTER_TEST ; cannam@85: cannam@85: static double snr_test (SINGLE_TEST *snr_test_data, int number, int converter, int verbose) ; cannam@85: static double find_peak (float *output, int output_len) ; cannam@85: static double bandwidth_test (int converter, int verbose) ; cannam@85: cannam@85: int cannam@85: main (int argc, char *argv []) cannam@85: { CONVERTER_TEST snr_test_data [] = cannam@85: { cannam@85: { SRC_ZERO_ORDER_HOLD, cannam@85: 8, cannam@85: BOOLEAN_FALSE, cannam@85: { { 1, { 0.01111111111 }, 3.0, 1, 28.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.6, 1, 36.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.3, 1, 36.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.0, 1, 150.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.001, 1, 38.0, 1.0 }, cannam@85: { 2, { 0.011111, 0.324 }, 1.9999, 2, 14.0, 1.0 }, cannam@85: { 2, { 0.012345, 0.457 }, 0.456789, 1, 12.0, 1.0 }, cannam@85: { 1, { 0.3511111111 }, 1.33, 1, 10.0, 1.0 } cannam@85: } cannam@85: }, cannam@85: cannam@85: { SRC_LINEAR, cannam@85: 8, cannam@85: BOOLEAN_FALSE, cannam@85: { { 1, { 0.01111111111 }, 3.0, 1, 73.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.6, 1, 73.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.3, 1, 73.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.0, 1, 150.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.001, 1, 77.0, 1.0 }, cannam@85: { 2, { 0.011111, 0.324 }, 1.9999, 2, 16.0, 0.94 }, cannam@85: { 2, { 0.012345, 0.457 }, 0.456789, 1, 26.0, 0.96 }, cannam@85: { 1, { 0.3511111111 }, 1.33, 1, 22.0, 0.99 } cannam@85: } cannam@85: }, cannam@85: cannam@85: { SRC_SINC_FASTEST, cannam@85: 9, cannam@85: BOOLEAN_TRUE, cannam@85: { { 1, { 0.01111111111 }, 3.0, 1, 100.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.6, 1, 99.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.3, 1, 100.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.0, 1, 150.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.001, 1, 100.0, 1.0 }, cannam@85: { 2, { 0.011111, 0.324 }, 1.9999, 2, 97.0, 1.0 }, cannam@85: { 2, { 0.012345, 0.457 }, 0.456789, 1, 100.0, 0.5 }, cannam@85: { 2, { 0.011111, 0.45 }, 0.6, 1, 97.0, 0.5 }, cannam@85: { 1, { 0.3511111111 }, 1.33, 1, 97.0, 1.0 } cannam@85: } cannam@85: }, cannam@85: cannam@85: { SRC_SINC_MEDIUM_QUALITY, cannam@85: 9, cannam@85: BOOLEAN_TRUE, cannam@85: { { 1, { 0.01111111111 }, 3.0, 1, 145.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.6, 1, 132.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.3, 1, 138.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.0, 1, 157.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.001, 1, 148.0, 1.0 }, cannam@85: { 2, { 0.011111, 0.324 }, 1.9999, 2, 127.0, 1.0 }, cannam@85: { 2, { 0.012345, 0.457 }, 0.456789, 1, 124.0, 0.5 }, cannam@85: { 2, { 0.011111, 0.45 }, 0.6, 1, 126.0, 0.5 }, cannam@85: { 1, { 0.43111111111 }, 1.33, 1, 121.0, 1.0 } cannam@85: } cannam@85: }, cannam@85: cannam@85: { SRC_SINC_BEST_QUALITY, cannam@85: 9, cannam@85: BOOLEAN_TRUE, cannam@85: { { 1, { 0.01111111111 }, 3.0, 1, 149.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.6, 1, 147.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 0.3, 1, 148.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.0, 1, 155.0, 1.0 }, cannam@85: { 1, { 0.01111111111 }, 1.001, 1, 148.0, 1.0 }, cannam@85: { 2, { 0.011111, 0.324 }, 1.9999, 2, 147.0, 1.0 }, cannam@85: { 2, { 0.012345, 0.457 }, 0.456789, 1, 148.0, 0.5 }, cannam@85: { 2, { 0.011111, 0.45 }, 0.6, 1, 149.0, 0.5 }, cannam@85: { 1, { 0.43111111111 }, 1.33, 1, 145.0, 1.0 } cannam@85: } cannam@85: }, cannam@85: } ; /* snr_test_data */ cannam@85: cannam@85: double best_snr, snr, freq3dB ; cannam@85: int j, k, converter, verbose = 0 ; cannam@85: cannam@85: if (argc == 2 && strcmp (argv [1], "--verbose") == 0) cannam@85: verbose = 1 ; cannam@85: cannam@85: puts ("") ; cannam@85: cannam@85: for (j = 0 ; j < ARRAY_LEN (snr_test_data) ; j++) cannam@85: { best_snr = 5000.0 ; cannam@85: cannam@85: converter = snr_test_data [j].converter ; cannam@85: cannam@85: printf (" Converter %d : %s\n", converter, src_get_name (converter)) ; cannam@85: printf (" %s\n", src_get_description (converter)) ; cannam@85: cannam@85: for (k = 0 ; k < snr_test_data [j].tests ; k++) cannam@85: { snr = snr_test (&(snr_test_data [j].test_data [k]), k, converter, verbose) ; cannam@85: if (best_snr > snr) cannam@85: best_snr = snr ; cannam@85: } ; cannam@85: cannam@85: printf (" Worst case Signal-to-Noise Ratio : %.2f dB.\n", best_snr) ; cannam@85: cannam@85: if (snr_test_data [j].do_bandwidth_test == BOOLEAN_FALSE) cannam@85: { puts (" Bandwith test not performed on this converter.\n") ; cannam@85: continue ; cannam@85: } cannam@85: cannam@85: freq3dB = bandwidth_test (converter, verbose) ; cannam@85: cannam@85: printf (" Measured -3dB rolloff point : %5.2f %%.\n\n", freq3dB) ; cannam@85: } ; cannam@85: cannam@85: return 0 ; cannam@85: } /* main */ cannam@85: cannam@85: /*============================================================================== cannam@85: */ cannam@85: cannam@85: static double cannam@85: snr_test (SINGLE_TEST *test_data, int number, int converter, int verbose) cannam@85: { static float data [BUFFER_LEN + 1] ; cannam@85: static float output [MAX_SPEC_LEN] ; cannam@85: cannam@85: SRC_STATE *src_state ; cannam@85: SRC_DATA src_data ; cannam@85: cannam@85: double output_peak, snr ; cannam@85: int k, output_len, input_len, error ; cannam@85: cannam@85: if (verbose != 0) cannam@85: { printf ("\tSignal-to-Noise Ratio Test %d.\n" cannam@85: "\t=====================================\n", number) ; cannam@85: printf ("\tFrequencies : [ ") ; cannam@85: for (k = 0 ; k < test_data->freq_count ; k++) cannam@85: printf ("%6.4f ", test_data->freqs [k]) ; cannam@85: cannam@85: printf ("]\n\tSRC Ratio : %8.4f\n", test_data->src_ratio) ; cannam@85: } cannam@85: else cannam@85: { printf ("\tSignal-to-Noise Ratio Test %d : ", number) ; cannam@85: fflush (stdout) ; cannam@85: } ; cannam@85: cannam@85: /* Set up the output array. */ cannam@85: if (test_data->src_ratio >= 1.0) cannam@85: { output_len = MAX_SPEC_LEN ; cannam@85: input_len = (int) ceil (MAX_SPEC_LEN / test_data->src_ratio) ; cannam@85: if (input_len > BUFFER_LEN) cannam@85: input_len = BUFFER_LEN ; cannam@85: } cannam@85: else cannam@85: { input_len = BUFFER_LEN ; cannam@85: output_len = (int) ceil (BUFFER_LEN * test_data->src_ratio) ; cannam@85: output_len &= ((-1) << 4) ; cannam@85: if (output_len > MAX_SPEC_LEN) cannam@85: output_len = MAX_SPEC_LEN ; cannam@85: input_len = (int) ceil (output_len / test_data->src_ratio) ; cannam@85: } ; cannam@85: cannam@85: memset (output, 0, sizeof (output)) ; cannam@85: cannam@85: /* Generate input data array. */ cannam@85: gen_windowed_sines (test_data->freq_count, test_data->freqs, 1.0, data, input_len) ; cannam@85: cannam@85: /* Perform sample rate conversion. */ cannam@85: if ((src_state = src_new (converter, 1, &error)) == NULL) cannam@85: { printf ("\n\nLine %d : src_new() failed : %s.\n\n", __LINE__, src_strerror (error)) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: src_data.end_of_input = 1 ; /* Only one buffer worth of input. */ cannam@85: cannam@85: src_data.data_in = data ; cannam@85: src_data.input_frames = input_len ; cannam@85: cannam@85: src_data.src_ratio = test_data->src_ratio ; cannam@85: cannam@85: src_data.data_out = output ; cannam@85: src_data.output_frames = output_len ; cannam@85: cannam@85: if ((error = src_process (src_state, &src_data))) cannam@85: { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: src_state = src_delete (src_state) ; cannam@85: cannam@85: if (verbose != 0) cannam@85: printf ("\tOutput Len : %ld\n", src_data.output_frames_gen) ; cannam@85: cannam@85: if (abs (src_data.output_frames_gen - output_len) > 4) cannam@85: { printf ("\n\nLine %d : output data length should be %d.\n\n", __LINE__, output_len) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: /* Check output peak. */ cannam@85: output_peak = find_peak (output, src_data.output_frames_gen) ; cannam@85: cannam@85: if (verbose != 0) cannam@85: printf ("\tOutput Peak : %6.4f\n", output_peak) ; cannam@85: cannam@85: if (fabs (output_peak - test_data->peak_value) > 0.01) cannam@85: { printf ("\n\nLine %d : output peak (%6.4f) should be %6.4f\n\n", __LINE__, output_peak, test_data->peak_value) ; cannam@85: save_oct_float ("snr_test.dat", data, BUFFER_LEN, output, output_len) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: /* Calculate signal-to-noise ratio. */ cannam@85: snr = calculate_snr (output, src_data.output_frames_gen, test_data->pass_band_peaks) ; cannam@85: cannam@85: if (snr < 0.0) cannam@85: { /* An error occurred. */ cannam@85: save_oct_float ("snr_test.dat", data, BUFFER_LEN, output, src_data.output_frames_gen) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: if (verbose != 0) cannam@85: printf ("\tSNR Ratio : %.2f dB\n", snr) ; cannam@85: cannam@85: if (snr < test_data->snr) cannam@85: { printf ("\n\nLine %d : SNR (%5.2f) should be > %6.2f dB\n\n", __LINE__, snr, test_data->snr) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: if (verbose != 0) cannam@85: puts ("\t-------------------------------------\n\tPass\n") ; cannam@85: else cannam@85: puts ("Pass") ; cannam@85: cannam@85: return snr ; cannam@85: } /* snr_test */ cannam@85: cannam@85: static double cannam@85: find_peak (float *data, int len) cannam@85: { double peak = 0.0 ; cannam@85: int k = 0 ; cannam@85: cannam@85: for (k = 0 ; k < len ; k++) cannam@85: if (fabs (data [k]) > peak) cannam@85: peak = fabs (data [k]) ; cannam@85: cannam@85: return peak ; cannam@85: } /* find_peak */ cannam@85: cannam@85: cannam@85: static double cannam@85: find_attenuation (double freq, int converter, int verbose) cannam@85: { static float input [BUFFER_LEN] ; cannam@85: static float output [2 * BUFFER_LEN] ; cannam@85: cannam@85: SRC_DATA src_data ; cannam@85: double output_peak ; cannam@85: int error ; cannam@85: cannam@85: gen_windowed_sines (1, &freq, 1.0, input, BUFFER_LEN) ; cannam@85: cannam@85: src_data.end_of_input = 1 ; /* Only one buffer worth of input. */ cannam@85: cannam@85: src_data.data_in = input ; cannam@85: src_data.input_frames = BUFFER_LEN ; cannam@85: cannam@85: src_data.src_ratio = 1.999 ; cannam@85: cannam@85: src_data.data_out = output ; cannam@85: src_data.output_frames = ARRAY_LEN (output) ; cannam@85: cannam@85: if ((error = src_simple (&src_data, converter, 1))) cannam@85: { printf ("\n\nLine %d : %s\n\n", __LINE__, src_strerror (error)) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: output_peak = find_peak (output, ARRAY_LEN (output)) ; cannam@85: cannam@85: if (verbose) cannam@85: printf ("\tFreq : %6f InPeak : %6f OutPeak : %6f Atten : %6.2f dB\n", cannam@85: freq, 1.0, output_peak, 20.0 * log10 (1.0 / output_peak)) ; cannam@85: cannam@85: return 20.0 * log10 (1.0 / output_peak) ; cannam@85: } /* find_attenuation */ cannam@85: cannam@85: static double cannam@85: bandwidth_test (int converter, int verbose) cannam@85: { double f1, f2, a1, a2 ; cannam@85: double freq, atten ; cannam@85: cannam@85: f1 = 0.35 ; cannam@85: a1 = find_attenuation (f1, converter, verbose) ; cannam@85: cannam@85: f2 = 0.495 ; cannam@85: a2 = find_attenuation (f2, converter, verbose) ; cannam@85: cannam@85: if (a1 > 3.0 || a2 < 3.0) cannam@85: { printf ("\n\nLine %d : cannot bracket 3dB point.\n\n", __LINE__) ; cannam@85: exit (1) ; cannam@85: } ; cannam@85: cannam@85: while (a2 - a1 > 1.0) cannam@85: { freq = f1 + 0.5 * (f2 - f1) ; cannam@85: atten = find_attenuation (freq, converter, verbose) ; cannam@85: cannam@85: if (atten < 3.0) cannam@85: { f1 = freq ; cannam@85: a1 = atten ; cannam@85: } cannam@85: else cannam@85: { f2 = freq ; cannam@85: a2 = atten ; cannam@85: } ; cannam@85: } ; cannam@85: cannam@85: freq = f1 + (3.0 - a1) * (f2 - f1) / (a2 - a1) ; cannam@85: cannam@85: return 200.0 * freq ; cannam@85: } /* bandwidth_test */ cannam@85: cannam@85: #else /* (HAVE_FFTW3) == 0 */ cannam@85: cannam@85: /* Alternative main function when librfftw is not available. */ cannam@85: cannam@85: int cannam@85: main (void) cannam@85: { puts ("\n" cannam@85: "****************************************************************\n" cannam@85: " This test cannot be run without FFTW (http://www.fftw.org/).\n" cannam@85: " Both the real and the complex versions of the library are\n" cannam@85: " required.") ; cannam@85: puts ("****************************************************************\n") ; cannam@85: cannam@85: return 0 ; cannam@85: } /* main */ cannam@85: cannam@85: #endif cannam@85: