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