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