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@holtmann.org>
7 * Copyright (C) 2007-2008 Frederic Dalleau <fdalleau@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
sampletobits(short sample16,int verbose)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
calculate_rms_level(SNDFILE * sndref,SF_INFO * infosref,SNDFILE * sndtst,SF_INFO * infostst,int accuracy,char * csvname)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
check_absolute_diff(SNDFILE * sndref,SF_INFO * infosref,SNDFILE * sndtst,SF_INFO * infostst,int accuracy)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
usage(void)248 static void usage(void)
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
main(int argc,char * argv[])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