Home | History | Annotate | Download | only in sbc
      1 /*
      2  *
      3  *  Bluetooth low-complexity, subband codec (SBC) library
      4  *
      5  *  Copyright (C) 2008-2010  Nokia Corporation
      6  *  Copyright (C) 2007-2010  Marcel Holtmann <marcel (at) holtmann.org>
      7  *  Copyright (C) 2007-2008  Frederic Dalleau <fdalleau (at) free.fr>
      8  *
      9  *
     10  *  This program is free software; you can redistribute it and/or modify
     11  *  it under the terms of the GNU General Public License as published by
     12  *  the Free Software Foundation; either version 2 of the License, or
     13  *  (at your option) any later version.
     14  *
     15  *  This program is distributed in the hope that it will be useful,
     16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *  GNU General Public License for more details.
     19  *
     20  *  You should have received a copy of the GNU General Public License
     21  *  along with this program; if not, write to the Free Software
     22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     23  *
     24  */
     25 
     26 #ifdef HAVE_CONFIG_H
     27 #include <config.h>
     28 #endif
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <sndfile.h>
     33 #include <math.h>
     34 #include <string.h>
     35 
     36 #define MAXCHANNELS 2
     37 #define DEFACCURACY 7
     38 
     39 static double sampletobits(short sample16, int verbose)
     40 {
     41 	double bits = 0;
     42 	unsigned short bit;
     43 	int i;
     44 
     45 	if (verbose)
     46 		printf("===> sampletobits(%hd, %04hX)\n", sample16, sample16);
     47 
     48 	/* Bit 0 is MSB */
     49 	if (sample16 < 0)
     50 		bits = -1;
     51 
     52 	if (verbose)
     53 		printf("%d", (sample16 < 0) ? 1 : 0);
     54 
     55 	/* Bit 15 is LSB */
     56 	for (i = 1; i < 16; i++) {
     57 		bit = (unsigned short) sample16;
     58 		bit >>= 15 - i;
     59 		bit %= 2;
     60 
     61 		if (verbose)
     62 			printf("%d", bit);
     63 
     64 		if (bit)
     65 			bits += (1.0 / pow(2.0, i));
     66 	}
     67 
     68 	if (verbose)
     69 		printf("\n");
     70 
     71 	return bits;
     72 }
     73 
     74 static int calculate_rms_level(SNDFILE * sndref, SF_INFO * infosref,
     75 				SNDFILE * sndtst, SF_INFO * infostst,
     76 						int accuracy, char *csvname)
     77 {
     78 	short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
     79 	double refbits, tstbits;
     80 	double rms_accu[MAXCHANNELS];
     81 	double rms_level[MAXCHANNELS];
     82 	double rms_limit = 1.0 / (pow(2.0, accuracy - 1) * pow(12.0, 0.5));
     83 	FILE *csv = NULL;
     84 	int i, j, r1, r2, verdict;
     85 
     86 	if (csvname)
     87 		csv = fopen(csvname, "wt");
     88 
     89 	if (csv) {
     90 		fprintf(csv, "num;");
     91 		for (j = 0; j < infostst->channels; j++)
     92 			fprintf(csv, "ref channel %d;tst channel %d;", j, j);
     93 		fprintf(csv, "\r\n");
     94 	}
     95 
     96 	sf_seek(sndref, 0, SEEK_SET);
     97 	sf_seek(sndtst, 0, SEEK_SET);
     98 
     99 	memset(rms_accu, 0, sizeof(rms_accu));
    100 	memset(rms_level, 0, sizeof(rms_level));
    101 
    102 	for (i = 0; i < infostst->frames; i++) {
    103 		if (csv)
    104 			fprintf(csv, "%d;", i);
    105 
    106 		r1 = sf_read_short(sndref, refsample, infostst->channels);
    107 		if (r1 != infostst->channels) {
    108 			printf("Failed to read reference data: %s "
    109 					"(r1=%d, channels=%d)",
    110 					sf_strerror(sndref), r1,
    111 					infostst->channels);
    112 			if (csv)
    113 				fclose(csv);
    114 			return -1;
    115 		}
    116 
    117 		r2 = sf_read_short(sndtst, tstsample, infostst->channels);
    118 		if (r2 != infostst->channels) {
    119 			printf("Failed to read test data: %s "
    120 					"(r2=%d, channels=%d)\n",
    121 					sf_strerror(sndtst), r2,
    122 					infostst->channels);
    123 			if (csv)
    124 				fclose(csv);
    125 			return -1;
    126 		}
    127 
    128 		for (j = 0; j < infostst->channels; j++) {
    129 			if (csv)
    130 				fprintf(csv, "%d;%d;", refsample[j],
    131 						tstsample[j]);
    132 
    133 			refbits = sampletobits(refsample[j], 0);
    134 			tstbits = sampletobits(tstsample[j], 0);
    135 
    136 			rms_accu[j] += pow(tstbits - refbits, 2.0);
    137 		}
    138 
    139 		if (csv)
    140 			fprintf(csv, "\r\n");
    141 	}
    142 
    143 	printf("Limit: %f\n", rms_limit);
    144 
    145 	for (j = 0; j < infostst->channels; j++) {
    146 		printf("Channel %d\n", j);
    147 		printf("Accumulated %f\n", rms_accu[j]);
    148 		rms_accu[j] /= (double) infostst->frames;
    149 		printf("Accumulated / %f = %f\n", (double) infostst->frames,
    150 				rms_accu[j]);
    151 		rms_level[j] = sqrt(rms_accu[j]);
    152 		printf("Level = %f (%f x %f = %f)\n",
    153 				rms_level[j], rms_level[j], rms_level[j],
    154 						rms_level[j] * rms_level[j]);
    155 	}
    156 
    157 	verdict = 1;
    158 
    159 	for (j = 0; j < infostst->channels; j++) {
    160 		printf("Channel %d: %f\n", j, rms_level[j]);
    161 
    162 		if (rms_level[j] > rms_limit)
    163 			verdict = 0;
    164 	}
    165 
    166 	printf("%s return %d\n", __FUNCTION__, verdict);
    167 
    168 	return verdict;
    169 }
    170 
    171 static int check_absolute_diff(SNDFILE * sndref, SF_INFO * infosref,
    172 				SNDFILE * sndtst, SF_INFO * infostst,
    173 				int accuracy)
    174 {
    175 	short refsample[MAXCHANNELS], tstsample[MAXCHANNELS];
    176 	short refmax[MAXCHANNELS], tstmax[MAXCHANNELS];
    177 	double refbits, tstbits;
    178 	double rms_absolute = 1.0 / (pow(2, accuracy - 2));
    179 	double calc_max[MAXCHANNELS];
    180 	int calc_count = 0;
    181 	short r1, r2;
    182 	double cur_diff;
    183 	int i, j, verdict;
    184 
    185 	memset(&refmax, 0, sizeof(refmax));
    186 	memset(&tstmax, 0, sizeof(tstmax));
    187 	memset(&calc_max, 0, sizeof(calc_max));
    188 	memset(&refsample, 0, sizeof(refsample));
    189 	memset(&tstsample, 0, sizeof(tstsample));
    190 
    191 	sf_seek(sndref, 0, SEEK_SET);
    192 	sf_seek(sndtst, 0, SEEK_SET);
    193 
    194 	verdict = 1;
    195 
    196 	printf("Absolute max: %f\n", rms_absolute);
    197 	for (i = 0; i < infostst->frames; i++) {
    198 		r1 = sf_read_short(sndref, refsample, infostst->channels);
    199 
    200 		if (r1 != infostst->channels) {
    201 			printf("Failed to read reference data: %s "
    202 					"(r1=%d, channels=%d)",
    203 					sf_strerror(sndref), r1,
    204 					infostst->channels);
    205 			return -1;
    206 		}
    207 
    208 		r2 = sf_read_short(sndtst, tstsample, infostst->channels);
    209 		if (r2 != infostst->channels) {
    210 			printf("Failed to read test data: %s "
    211 					"(r2=%d, channels=%d)\n",
    212 					sf_strerror(sndtst), r2,
    213 					infostst->channels);
    214 			return -1;
    215 		}
    216 
    217 		for (j = 0; j < infostst->channels; j++) {
    218 			refbits = sampletobits(refsample[j], 0);
    219 			tstbits = sampletobits(tstsample[j], 0);
    220 
    221 			cur_diff = fabs(tstbits - refbits);
    222 
    223 			if (cur_diff > rms_absolute) {
    224 				calc_count++;
    225 				/* printf("Channel %d exceeded : fabs(%f - %f) = %f > %f\n", j, tstbits, refbits, cur_diff, rms_absolute); */
    226 				verdict = 0;
    227 			}
    228 
    229 			if (cur_diff > calc_max[j]) {
    230 				calc_max[j] = cur_diff;
    231 				refmax[j] = refsample[j];
    232 				tstmax[j] = tstsample[j];
    233 			}
    234 		}
    235 	}
    236 
    237 	for (j = 0; j < infostst->channels; j++) {
    238 		printf("Calculated max: %f (%hd-%hd=%hd)\n",
    239 			calc_max[j], tstmax[j], refmax[j],
    240 			tstmax[j] - refmax[j]);
    241 	}
    242 
    243 	printf("%s return %d\n", __FUNCTION__, verdict);
    244 
    245 	return verdict;
    246 }
    247 
    248 static void usage()
    249 {
    250 	printf("SBC conformance test ver %s\n", VERSION);
    251 	printf("Copyright (c) 2007-2010  Marcel Holtmann\n");
    252 	printf("Copyright (c) 2007-2008  Frederic Dalleau\n\n");
    253 
    254 	printf("Usage:\n"
    255 		"\tsbctester reference.wav checkfile.wav\n"
    256 		"\tsbctester integer\n"
    257 		"\n");
    258 
    259 	printf("To test the encoder:\n");
    260 	printf("\tUse a reference codec to encode original.wav to reference.sbc\n");
    261 	printf("\tUse sbcenc to encode original.wav to checkfile.sbc\n");
    262 	printf("\tDecode both file using the reference decoder\n");
    263 	printf("\tRun sbctester with these two wav files to get the result\n\n");
    264 
    265 	printf("\tA file called out.csv is generated to use the data in a\n");
    266 	printf("\tspreadsheet application or database.\n\n");
    267 }
    268 
    269 int main(int argc, char *argv[])
    270 {
    271 	SNDFILE *sndref = NULL;
    272 	SNDFILE *sndtst = NULL;
    273 	SF_INFO infosref;
    274 	SF_INFO infostst;
    275 	char *ref;
    276 	char *tst;
    277 	int pass_rms, pass_absolute, pass, accuracy;
    278 
    279 	if (argc == 2) {
    280 		double db;
    281 
    282 		printf("Test sampletobits\n");
    283 		db = sampletobits((short) atoi(argv[1]), 1);
    284 		printf("db = %f\n", db);
    285 		exit(0);
    286 	}
    287 
    288 	if (argc < 3) {
    289 		usage();
    290 		exit(1);
    291 	}
    292 
    293 	ref = argv[1];
    294 	tst = argv[2];
    295 
    296 	printf("opening reference %s\n", ref);
    297 
    298 	sndref = sf_open(ref, SFM_READ, &infosref);
    299 	if (!sndref) {
    300 		printf("Failed to open reference file\n");
    301 		exit(1);
    302 	}
    303 
    304 	printf("opening testfile %s\n", tst);
    305 	sndtst = sf_open(tst, SFM_READ, &infostst);
    306 	if (!sndtst) {
    307 		printf("Failed to open test file\n");
    308 		sf_close(sndref);
    309 		exit(1);
    310 	}
    311 
    312 	printf("reference:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
    313 		(int) infosref.frames, (int) infosref.samplerate,
    314 		(int) infosref.channels);
    315 	printf("testfile:\n\t%d frames,\n\t%d hz,\n\t%d channels\n",
    316 		(int) infostst.frames, (int) infostst.samplerate,
    317 		(int) infostst.channels);
    318 
    319 	/* check number of channels */
    320 	if (infosref.channels > 2 || infostst.channels > 2) {
    321 		printf("Too many channels\n");
    322 		goto error;
    323 	}
    324 
    325 	/* compare number of samples */
    326 	if (infosref.samplerate != infostst.samplerate ||
    327 				infosref.channels != infostst.channels) {
    328 		printf("Cannot compare files with different charasteristics\n");
    329 		goto error;
    330 	}
    331 
    332 	accuracy = DEFACCURACY;
    333 	printf("Accuracy: %d\n", accuracy);
    334 
    335 	/* Condition 1 rms level */
    336 	pass_rms = calculate_rms_level(sndref, &infosref, sndtst, &infostst,
    337 					accuracy, "out.csv");
    338 	if (pass_rms < 0)
    339 		goto error;
    340 
    341 	/* Condition 2 absolute difference */
    342 	pass_absolute = check_absolute_diff(sndref, &infosref, sndtst,
    343 						&infostst, accuracy);
    344 	if (pass_absolute < 0)
    345 		goto error;
    346 
    347 	/* Verdict */
    348 	pass = pass_rms && pass_absolute;
    349 	printf("Verdict: %s\n", pass ? "pass" : "fail");
    350 
    351 	return 0;
    352 
    353 error:
    354 	sf_close(sndref);
    355 	sf_close(sndtst);
    356 
    357 	exit(1);
    358 }
    359