diff options
author | Adrian Bunk <adrian.bunk@movial.com> | 2011-06-03 09:17:04 +0000 |
---|---|---|
committer | Adrian Bunk <adrian.bunk@movial.com> | 2011-06-03 09:17:04 +0000 |
commit | 799757ccf1d03c33c75bc597cd5ef77741dcb6a7 (patch) | |
tree | a8c3be85c730de28b012586591b76301033d3d21 /sbc/sbctester.c |
Imported upstream 4.91upstream-4.91upstreampackaging
Diffstat (limited to 'sbc/sbctester.c')
-rw-r--r-- | sbc/sbctester.c | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/sbc/sbctester.c b/sbc/sbctester.c new file mode 100644 index 0000000..b1e3608 --- /dev/null +++ b/sbc/sbctester.c @@ -0,0 +1,358 @@ +/* + * + * Bluetooth low-complexity, subband codec (SBC) library + * + * Copyright (C) 2008-2010 Nokia Corporation + * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org> + * Copyright (C) 2007-2008 Frederic Dalleau <fdalleau@free.fr> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <sndfile.h> +#include <math.h> +#include <string.h> + +#define MAXCHANNELS 2 +#define DEFACCURACY 7 + +static double sampletobits(short sample16, int verbose) +{ + double bits = 0; + unsigned short bit; + int i; + + if (verbose) + printf("===> sampletobits(%hd, %04hX)\n", sample16, sample16); + + /* Bit 0 is MSB */ + if (sample16 < 0) + bits = -1; + + if (verbose) + printf("%d", (sample16 < 0) ? 1 : 0); + + /* Bit 15 is LSB */ + for (i = 1; i < 16; i++) { + bit = (unsigned short) sample16; + bit >>= 15 - i; + bit %= 2; + + if (verbose) + printf("%d", bit); + + if (bit) + bits += (1.0 / pow(2.0, i)); + } + + if (verbose) + printf("\n"); + + return bits; +} + +static int calculate_rms_level(SNDFILE * sndref, SF_INFO * infosref, + SNDFILE * sndtst, SF_INFO * infostst, + int accuracy, char *csvname) +{ + short refsample[MAXCHANNELS], tstsample[MAXCHANNELS]; + double refbits, tstbits; + double rms_accu[MAXCHANNELS]; + double rms_level[MAXCHANNELS]; + double rms_limit = 1.0 / (pow(2.0, accuracy - 1) * pow(12.0, 0.5)); + FILE *csv = NULL; + int i, j, r1, r2, verdict; + + if (csvname) + csv = fopen(csvname, "wt"); + + if (csv) { + fprintf(csv, "num;"); + for (j = 0; j < infostst->channels; j++) + fprintf(csv, "ref channel %d;tst channel %d;", j, j); + fprintf(csv, "\r\n"); + } + + sf_seek(sndref, 0, SEEK_SET); + sf_seek(sndtst, 0, SEEK_SET); + + memset(rms_accu, 0, sizeof(rms_accu)); + memset(rms_level, 0, sizeof(rms_level)); + + for (i = 0; i < infostst->frames; i++) { + if (csv) + fprintf(csv, "%d;", i); + + r1 = sf_read_short(sndref, refsample, infostst->channels); + if (r1 != infostst->channels) { + printf("Failed to read reference data: %s " + "(r1=%d, channels=%d)", + sf_strerror(sndref), r1, + infostst->channels); + if (csv) + fclose(csv); + return -1; + } + + r2 = sf_read_short(sndtst, tstsample, infostst->channels); + if (r2 != infostst->channels) { + printf("Failed to read test data: %s " + "(r2=%d, channels=%d)\n", + sf_strerror(sndtst), r2, + infostst->channels); + if (csv) + fclose(csv); + return -1; + } + + for (j = 0; j < infostst->channels; j++) { + if (csv) + fprintf(csv, "%d;%d;", refsample[j], + tstsample[j]); + + refbits = sampletobits(refsample[j], 0); + tstbits = sampletobits(tstsample[j], 0); + + rms_accu[j] += pow(tstbits - refbits, 2.0); + } + + if (csv) + fprintf(csv, "\r\n"); + } + + printf("Limit: %f\n", rms_limit); + + for (j = 0; j < infostst->channels; j++) { + printf("Channel %d\n", j); + printf("Accumulated %f\n", rms_accu[j]); + rms_accu[j] /= (double) infostst->frames; + printf("Accumulated / %f = %f\n", (double) infostst->frames, + rms_accu[j]); + rms_level[j] = sqrt(rms_accu[j]); + printf("Level = %f (%f x %f = %f)\n", + rms_level[j], rms_level[j], rms_level[j], + rms_level[j] * rms_level[j]); + } + + verdict = 1; + + for (j = 0; j < infostst->channels; j++) { + printf("Channel %d: %f\n", j, rms_level[j]); + + if (rms_level[j] > rms_limit) + verdict = 0; + } + + printf("%s return %d\n", __FUNCTION__, verdict); + + return verdict; +} + +static int check_absolute_diff(SNDFILE * sndref, SF_INFO * infosref, + SNDFILE * sndtst, SF_INFO * infostst, + int accuracy) +{ + short refsample[MAXCHANNELS], tstsample[MAXCHANNELS]; + short refmax[MAXCHANNELS], tstmax[MAXCHANNELS]; + double refbits, tstbits; + double rms_absolute = 1.0 / (pow(2, accuracy - 2)); + double calc_max[MAXCHANNELS]; + int calc_count = 0; + short r1, r2; + double cur_diff; + int i, j, verdict; + + memset(&refmax, 0, sizeof(refmax)); + memset(&tstmax, 0, sizeof(tstmax)); + memset(&calc_max, 0, sizeof(calc_max)); + memset(&refsample, 0, sizeof(refsample)); + memset(&tstsample, 0, sizeof(tstsample)); + + sf_seek(sndref, 0, SEEK_SET); + sf_seek(sndtst, 0, SEEK_SET); + + verdict = 1; + + printf("Absolute max: %f\n", rms_absolute); + for (i = 0; i < infostst->frames; i++) { + r1 = sf_read_short(sndref, refsample, infostst->channels); + + if (r1 != infostst->channels) { + printf("Failed to read reference data: %s " + "(r1=%d, channels=%d)", + sf_strerror(sndref), r1, + infostst->channels); + return -1; + } + + r2 = sf_read_short(sndtst, tstsample, infostst->channels); + if (r2 != infostst->channels) { + printf("Failed to read test data: %s " + "(r2=%d, channels=%d)\n", + sf_strerror(sndtst), r2, + infostst->channels); + return -1; + } + + for (j = 0; j < infostst->channels; j++) { + refbits = sampletobits(refsample[j], 0); + tstbits = sampletobits(tstsample[j], 0); + + cur_diff = fabs(tstbits - refbits); + + if (cur_diff > rms_absolute) { + calc_count++; + /* printf("Channel %d exceeded : fabs(%f - %f) = %f > %f\n", j, tstbits, refbits, cur_diff, rms_absolute); */ + verdict = 0; + } + + if (cur_diff > calc_max[j]) { + calc_max[j] = cur_diff; + refmax[j] = refsample[j]; + tstmax[j] = tstsample[j]; + } + } + } + + for (j = 0; j < infostst->channels; j++) { + printf("Calculated max: %f (%hd-%hd=%hd)\n", + calc_max[j], tstmax[j], refmax[j], + tstmax[j] - refmax[j]); + } + + printf("%s return %d\n", __FUNCTION__, verdict); + + return verdict; +} + +static void usage() +{ + printf("SBC conformance test ver %s\n", VERSION); + printf("Copyright (c) 2007-2010 Marcel Holtmann\n"); + printf("Copyright (c) 2007-2008 Frederic Dalleau\n\n"); + + printf("Usage:\n" + "\tsbctester reference.wav checkfile.wav\n" + "\tsbctester integer\n" + "\n"); + + printf("To test the encoder:\n"); + printf("\tUse a reference codec to encode original.wav to reference.sbc\n"); + printf("\tUse sbcenc to encode original.wav to checkfile.sbc\n"); + printf("\tDecode both file using the reference decoder\n"); + printf("\tRun sbctester with these two wav files to get the result\n\n"); + + printf("\tA file called out.csv is generated to use the data in a\n"); + printf("\tspreadsheet application or database.\n\n"); +} + +int main(int argc, char *argv[]) +{ + SNDFILE *sndref = NULL; + SNDFILE *sndtst = NULL; + SF_INFO infosref; + SF_INFO infostst; + char *ref; + char *tst; + int pass_rms, pass_absolute, pass, accuracy; + + if (argc == 2) { + double db; + + printf("Test sampletobits\n"); + db = sampletobits((short) atoi(argv[1]), 1); + printf("db = %f\n", db); + exit(0); + } + + if (argc < 3) { + usage(); + exit(1); + } + + ref = argv[1]; + tst = argv[2]; + + printf("opening reference %s\n", ref); + + sndref = sf_open(ref, SFM_READ, &infosref); + if (!sndref) { + printf("Failed to open reference file\n"); + exit(1); + } + + printf("opening testfile %s\n", tst); + sndtst = sf_open(tst, SFM_READ, &infostst); + if (!sndtst) { + printf("Failed to open test file\n"); + sf_close(sndref); + exit(1); + } + + printf("reference:\n\t%d frames,\n\t%d hz,\n\t%d channels\n", + (int) infosref.frames, (int) infosref.samplerate, + (int) infosref.channels); + printf("testfile:\n\t%d frames,\n\t%d hz,\n\t%d channels\n", + (int) infostst.frames, (int) infostst.samplerate, + (int) infostst.channels); + + /* check number of channels */ + if (infosref.channels > 2 || infostst.channels > 2) { + printf("Too many channels\n"); + goto error; + } + + /* compare number of samples */ + if (infosref.samplerate != infostst.samplerate || + infosref.channels != infostst.channels) { + printf("Cannot compare files with different charasteristics\n"); + goto error; + } + + accuracy = DEFACCURACY; + printf("Accuracy: %d\n", accuracy); + + /* Condition 1 rms level */ + pass_rms = calculate_rms_level(sndref, &infosref, sndtst, &infostst, + accuracy, "out.csv"); + if (pass_rms < 0) + goto error; + + /* Condition 2 absolute difference */ + pass_absolute = check_absolute_diff(sndref, &infosref, sndtst, + &infostst, accuracy); + if (pass_absolute < 0) + goto error; + + /* Verdict */ + pass = pass_rms && pass_absolute; + printf("Verdict: %s\n", pass ? "pass" : "fail"); + + return 0; + +error: + sf_close(sndref); + sf_close(sndtst); + + exit(1); +} |