diff options
| author | Brad Midgley <bmidgley@xmission.com> | 2007-10-25 02:25:34 +0000 | 
|---|---|---|
| committer | Brad Midgley <bmidgley@xmission.com> | 2007-10-25 02:25:34 +0000 | 
| commit | 5a3f10131eaeaa9336b8f8c501e0051a2ee69ec5 (patch) | |
| tree | 5ef471649a3a429beaacaf5c05039bfe60e0a3bb | |
| parent | 013e2efc3a57ad9150eea9bb653ed8b8e3d02bcc (diff) | |
Frederic's conformance tests
it needs build system integration, including -lsndfile to link
this app should compile only if libsndfile-dev is present
'make install' should ignore it
| -rw-r--r-- | sbc/sbctester.c | 358 | 
1 files changed, 358 insertions, 0 deletions
| diff --git a/sbc/sbctester.c b/sbc/sbctester.c new file mode 100644 index 00000000..55ab28c0 --- /dev/null +++ b/sbc/sbctester.c @@ -0,0 +1,358 @@ +/*                                                                         * + *   Copyright (C) 2007 by Frederic Dalleau                                * + *   fdalleau@free.fr                                                      * + *                                                                         * + *   This program is free software; you can redistribute it and/or modify  * + *   it under the terms of the GNU General Public License as published by  * + *   the Free Software Foundation; either version 2 of the License, or     * + *   (at your option) any later version.                                   * + *                                                                         * + *   This program is distributed in the hope that it will be useful,       * + *   but WITHOUT ANY WARRANTY; without even the implied warranty of        * + *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * + *   GNU General Public License for more details.                          * + *                                                                         * + *   You should have received a copy of the GNU General Public License     * + *   along with this program; if not, write to the                         * + *   Free Software Foundation, Inc.,                                       * + *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * + ***************************************************************************/ + +/* + +A2DP Test Specification Chapter 4.6 (p 25) +namely SBC codec conformance test +This is a test procedure for SBC + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <sndfile.h> +#include <math.h> +#include <string.h> + +#define MAXCHANNELS 2 +#define MAXFRAMESTESTED infostst->frames +#define TSTSAMPLEFACTOR(x) (x) +#define DEFACCURACY 7 + +/* temporary */ +#ifndef VERSION +#define VERSION "1" +#endif + +void usage() +{ +	printf("SBC codec conformance test (see Chapter 4.6, p. 25) ver %s\n", VERSION); +	printf("Copyright (c) 2007 Frederic Dalleau\n\n"); + +	//printf("This is a mandatory test case, but alternative methods exists.\n\n"); + +	printf("Usage:\n" +		"\tsbctester reference.wav checkfile.wav\n" +		"\tsbctester integer\n" +		"\n"); + +	printf("\tTo test the encoder:\n"); +	printf("\tUse a reference codec to encode original.wav to reference.sbc\n"); +	printf("\tUse sbcenc to encode original.wav to checkfile.sbc\n"); +	printf("\tDecode both file using the reference decoder\n"); +	printf("\trun sbctester with these two wav files to get the result\n"); + +	printf("\n\tA file called out.csv is generated to use the data in a spreadsheet application or database.\n\n"); +} + +double sampletobits(short sample16, int verbose) +{ +	double bits = 0; +	int i; +	unsigned short bit; + +	if (verbose) +		printf("=======> sampletobits(%hd, %04hX)\n", sample16, sample16); + +	// Bit 0 is MSB +	if (sample16 < 0) +		bits = -1; + +	if (verbose) +		printf("%d", (sample16 < 0) ? 1 : 0); + +	// Bit 15 is LSB +	for (i = 1; i < 16; i++) { +		bit = (unsigned short) sample16; +		bit >>= 15 - i; +		bit %= 2; + +		if (verbose) +			printf("%d", bit); + +		if (bit) +			bits += (1.0 / pow(2.0, i)); +	} + +	if (verbose) +		printf("\n"); +	return bits; +} + +int calculate_rms_level(SNDFILE * sndref, SF_INFO * infosref, SNDFILE * sndtst, SF_INFO * infostst, int accuracy, char *csvname) +{ +	int i, j, err = 0, verdict = 0; +	short refsample[MAXCHANNELS], tstsample[MAXCHANNELS]; +	double refbits, tstbits; +	double rms; +	double rms_accu[MAXCHANNELS]; +	double rms_level[MAXCHANNELS]; +	double rms_limit = 1.0 / (pow(2.0, accuracy - 1) * pow(12.0, 0.5)); +	FILE *csv = NULL; +	int r1, r2; + +	if (csvname) +		csv = fopen(csvname, "wt"); + +	if (csv) { +		fprintf(csv, "num;"); +		for (j = 0; j < infostst->channels; j++) +			fprintf(csv, "ref channel %d;tst channel %d;", j, j); +		fprintf(csv, "\r\n"); +	} + +	sf_seek(sndref, 0, SEEK_SET); +	sf_seek(sndtst, 0, SEEK_SET); +	memset(rms_accu, 0, sizeof(rms_accu)); +	memset(rms_level, 0, sizeof(rms_level)); + +	for (i = 0; i < MAXFRAMESTESTED; i++) { +		if (csv) +			fprintf(csv, "%d;", i); + +		r1 = sf_read_short(sndref, refsample, infostst->channels); +		if (r1 != infostst->channels) { +			printf("Failed to read reference data:%s (r1=%d, channels=%d)", sf_strerror(sndref), r1, infostst->channels); +			err = -1; +			goto error; +		} + +		r2 = sf_read_short(sndtst, tstsample, infostst->channels); +		if (r2 != infostst->channels) { +			printf("Failed to read test data:%s (r2=%d, channels=%d)\n", sf_strerror(sndtst), r2, infostst->channels); +			err = -1; +			goto error; +		} + +		for (j = 0; j < infostst->channels; j++) { +			if (csv) +				fprintf(csv, "%d;%d;", refsample[j], tstsample[j]); + +			refbits = sampletobits(refsample[j], 0); +			tstbits = sampletobits(TSTSAMPLEFACTOR(tstsample[j]), 0); + +			rms_accu[j] += pow(tstbits - refbits, 2.0); +		} + +		if (csv) +			fprintf(csv, "\r\n"); +	} + +	printf("Limit: %f\n", rms_limit); + +	for (j = 0; j < infostst->channels; j++) { +		printf("Channel %d\n", j); +		printf("Accumulated %f\n", rms_accu[j]); +		rms_accu[j] /= (double) infostst->frames; +		printf("Accumulated / %f = %f\n", (double) infostst->frames, rms_accu[j]); +		rms_level[j] = sqrt(rms_accu[j]); +		printf("Level = %f (%f x %f = %f)\n", rms_level[j], rms_level[j], rms_level[j], rms_level[j] * rms_level[j]); +	} + +	verdict = 1; +	for (j = 0; j < infostst->channels; j++) { +		printf("Channel %d: %f\n", j, rms_level[j]); + +		if (rms_level[j] > rms_limit) +			verdict = 0; +	} + +	printf("%s return %d\n", __FUNCTION__, verdict); + + +      error: + +	if (csv) +		fclose(csv); + +	return (err < 0) ? err : verdict; +} + +int check_sample() +{ +	return 0; +} + +int check_absolute_diff(SNDFILE * sndref, SF_INFO * infosref, SNDFILE * sndtst, SF_INFO * infostst, int accuracy) +{ +	int i, j, err = 0, verdict = 0; +	short refsample[MAXCHANNELS], tstsample[MAXCHANNELS], refmax[MAXCHANNELS], tstmax[MAXCHANNELS]; +	double refbits, tstbits; +	double rms_absolute = 1.0 / (pow(2, accuracy - 2)); +	double calc_max[MAXCHANNELS]; +	int calc_count = 0; +	short r1, r2; +	double cur_diff; + +	memset(&refmax, 0, sizeof(refmax)); +	memset(&tstmax, 0, sizeof(tstmax)); +	memset(&calc_max, 0, sizeof(calc_max)); +	memset(&refsample, 0, sizeof(refsample)); +	memset(&tstsample, 0, sizeof(tstsample)); + +	verdict = 1; +	sf_seek(sndref, 0, SEEK_SET); +	sf_seek(sndtst, 0, SEEK_SET); + +	printf("Absolute max: %f\n", rms_absolute); +	for (i = 0; i < MAXFRAMESTESTED; i++) { +		r1 = sf_read_short(sndref, refsample, infostst->channels); + +		if (r1 != infostst->channels) { +			printf("Failed to read reference data:%s (r1=%d, channels=%d)", sf_strerror(sndref), r1, infostst->channels); +			err = -1; +			goto error; +		} + +		r2 = sf_read_short(sndtst, tstsample, infostst->channels); +		if (r2 != infostst->channels) { +			printf("Failed to read test data:%s (r2=%d, channels=%d)\n", sf_strerror(sndtst), r2, infostst->channels); +			err = -1; +			goto error; +		} + +		for (j = 0; j < infostst->channels; j++) { +			refbits = sampletobits(refsample[j], 0); +			tstbits = sampletobits(TSTSAMPLEFACTOR(tstsample[j]), 0); + +			cur_diff = fabs(tstbits - refbits); + +			if (cur_diff > rms_absolute) { +				calc_count++; +				//printf("Channel %d exceeded : fabs(%f - %f) = %f > %f\n", j, tstbits, refbits, cur_diff, rms_absolute); +				verdict = 0; +			} +			if (cur_diff > calc_max[j]) { +				calc_max[j] = cur_diff; +				refmax[j] = refsample[j]; +				tstmax[j] = tstsample[j]; +			} +		} +	} + +	for (j = 0; j < infostst->channels; j++) { +		printf("Calculated max: %f (%hd-%hd=%hd)\n", calc_max[j], tstmax[j], refmax[j], tstmax[j] - refmax[j]); +	} + +	printf("%s return %d\n", __FUNCTION__, verdict); + +      error: + +	return (err < 0) ? err : verdict; +} + +int main(int argc, char *argv[]) +{ +	int err = 0; +	int rms_absolute, pass_rms, pass_absolute, pass, accuracy; +	char *ref; +	char *tst; +	SNDFILE *sndref = NULL; +	SNDFILE *sndtst = NULL; +	SF_INFO infosref; +	SF_INFO infostst; + +	if (argc == 2) { +		double db; + +		printf("Test sampletobits\n"); +		db = sampletobits((short) atoi(argv[1]), 1); +		printf("db = %f\n", db); +		exit(0); +	} + +	if (argc < 3) { +		usage(); +		exit(1); +	} + +	ref = argv[1]; +	tst = argv[2]; + +	// open both files +	printf("opening reference %s\n", ref); + +	sndref = sf_open(ref, SFM_READ, &infosref); +	if (!sndref) { +		printf("Failed to open reference file\n"); +		err = -1; +		goto error; +	} + +	printf("opening testfile %s\n", tst); +	sndtst = sf_open(tst, SFM_READ, &infostst); +	if (!sndtst) { +		printf("Failed to open test file\n"); +		err = -1; +		goto error; +	} + +	printf("reference:\n\t%d frames,\n\t%d hz,\n\t%d channels\n", (int) infosref.frames, (int) infosref.samplerate, (int) infosref.channels); +	printf("testfile:\n\t%d frames,\n\t%d hz,\n\t%d channels\n", (int) infostst.frames, (int) infostst.samplerate, (int) infostst.channels); + +	// check number of channels +	if (infosref.channels > 2 || infostst.channels > 2) { +		printf("Too many channels\n"); +		err = -1; +		goto error; +	} +	// compare number of samples +	if (infosref.samplerate != infostst.samplerate || infosref.channels != infostst.channels) { +		printf("Cannot compare files with different charasteristics\n"); +		err = -1; +		goto error; +	} + +	accuracy = DEFACCURACY; +	printf("Accuracy: %d\n", accuracy); + +	// Condition 1 rms level +	pass_rms = calculate_rms_level(sndref, &infosref, sndtst, &infostst, accuracy, "out.csv"); + +	if (pass_rms < 0) { +		err = pass_rms; +		goto error; +	} +	// Condition 2 absolute difference +	pass_absolute = check_absolute_diff(sndref, &infosref, sndtst, &infostst, accuracy); + +	if (pass_absolute < 0) { +		err = pass_absolute; +		goto error; +	} +	// Verdict +	pass = pass_rms && pass_absolute; +	printf("Verdict: %s\n", pass ? "pass" : "fail"); + +      error: + +	if (sndref) +		sf_close(sndref); + +	if (sndtst) +		sf_close(sndtst); + +	return err; +} | 
