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