view src/libsamplerate-0.1.9/tests/snr_bw_test.c @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 481f5f8c5634
children
line wrap: on
line source
/*
** 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