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(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 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