ref: 730fa4c5de29be500f4fbf041386b473d4319a3b
parent: 0917872a2019d5ecf9563b7bc150349272887ad4
author: Timothy Terriberry <[email protected]>
date: Thu Jun 16 11:54:20 EDT 2011
New C version of the Opus comparison tool
--- a/doc/build_draft.sh
+++ b/doc/build_draft.sh
@@ -35,13 +35,13 @@
echo building base64 version
cat opus_source.tar.gz| base64 -w 66 | sed 's/^/###/' > opus_source.base64
-echo '<figure>' > opus_compare_escaped.m
-echo '<artwork>' >> opus_compare_escaped.m
-echo '<![CDATA[' >> opus_compare_escaped.m
-cat opus_compare.m >> opus_compare_escaped.m
-echo ']]>' >> opus_compare_escaped.m
-echo '</artwork>' >> opus_compare_escaped.m
-echo '</figure>' >> opus_compare_escaped.m
+echo '<figure>' > opus_compare_escaped.c
+echo '<artwork>' >> opus_compare_escaped.c
+echo '<![CDATA[' >> opus_compare_escaped.c
+cat opus_compare.c >> opus_compare_escaped.c
+echo ']]>' >> opus_compare_escaped.c
+echo '</artwork>' >> opus_compare_escaped.c
+echo '</figure>' >> opus_compare_escaped.c
echo running xml2rfc
xml2rfc draft-ietf-codec-opus.xml
--- a/doc/draft-ietf-codec-opus.xml
+++ b/doc/draft-ietf-codec-opus.xml
@@ -53,7 +53,7 @@
</address>
</author>
-<date day="31" month="March" year="2011" />
+<date day="16" month="June" year="2011" />
<area>General</area>
@@ -4251,9 +4251,9 @@
</section>
-<section anchor="opus-compare" title="opus_compare.m">
+<section anchor="opus-compare" title="opus_compare.c">
<t>
-<?rfc include="opus_compare_escaped.m"?>
+<?rfc include="opus_compare_escaped.c"?>
</t>
</section>
--- /dev/null
+++ b/doc/opus_compare.c
@@ -1,0 +1,284 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define OPUS_PI (3.14159265F)
+
+#define OPUS_MIN(_x,_y) ((_x)<(_y)?(_x):(_y))
+#define OPUS_MAX(_x,_y) ((_x)>(_y)?(_x):(_y))
+#define OPUS_CLAMP(_a,_b,_c) OPUS_MAX(_a,OPUS_MIN(_b,_c))
+#define OPUS_COSF(_x) ((float)cos(_x))
+#define OPUS_SINF(_x) ((float)sin(_x))
+#define OPUS_SQRTF(_x) ((float)sqrt(_x))
+#define OPUS_LOG10F(_x) ((float)log10(_x))
+
+static void *check_alloc(void *_ptr){
+ if(_ptr==NULL){
+ fprintf(stderr,"Out of memory.\n");
+ exit(EXIT_FAILURE);
+ }
+ return _ptr;
+}
+
+static void *opus_malloc(size_t _size){
+ return check_alloc(malloc(_size));
+}
+
+static void *opus_realloc(void *_ptr,size_t _size){
+ return check_alloc(realloc(_ptr,_size));
+}
+
+static size_t read_pcm16(float **_samples,FILE *_fin,
+ int _nchannels){
+ unsigned char buf[1024];
+ float *samples;
+ size_t nsamples;
+ size_t csamples;
+ size_t xi;
+ size_t nread;
+ samples=NULL;
+ nsamples=csamples=0;
+ for(;;){
+ nread=fread(buf,2*_nchannels,1024/(2*_nchannels),_fin);
+ if(nread<=0)break;
+ if(nsamples+nread>csamples){
+ do csamples=csamples<<1|1;
+ while(nsamples+nread>csamples);
+ samples=(float *)opus_realloc(samples,
+ _nchannels*csamples*sizeof(*samples));
+ }
+ for(xi=0;xi<nread;xi++){
+ int ci;
+ for(ci=0;ci<_nchannels;ci++){
+ int s;
+ s=buf[2*(xi*_nchannels+ci)+1]<<8|buf[2*(xi*_nchannels+ci)];
+ s=((s&0xFFFF)^0x8000)-0x8000;
+ samples[(nsamples+xi)*_nchannels+ci]=s;
+ }
+ }
+ nsamples+=nread;
+ }
+ *_samples=(float *)opus_realloc(samples,
+ _nchannels*nsamples*sizeof(*samples));
+ return nsamples;
+}
+
+static void band_energy(float *_out,const int *_bands,int _nbands,
+ const float *_in,int _nchannels,size_t _nframes,int _window_sz,
+ int _step){
+ float *window;
+ float *x;
+ float *c;
+ float *s;
+ size_t xi;
+ int xj;
+ window=(float *)opus_malloc((3+_nchannels)*_window_sz
+ *sizeof(*window));
+ c=window+_window_sz;
+ s=c+_window_sz;
+ x=s+_window_sz;
+ for(xj=0;xj<_window_sz;xj++){
+ window[xj]=0.5F-0.5F*OPUS_COSF((2*OPUS_PI/(_window_sz-1))*xj);
+ }
+ for(xj=0;xj<_window_sz;xj++)
+ c[xj]=OPUS_COSF((2*OPUS_PI/_window_sz)*xj);
+ for(xj=0;xj<_window_sz;xj++)
+ s[xj]=OPUS_SINF((2*OPUS_PI/_window_sz)*xj);
+ for(xi=0;xi<_nframes;xi++){
+ int ci;
+ int xk;
+ int bi;
+ for(ci=0;ci<_nchannels;ci++){
+ for(xk=0;xk<_window_sz;xk++){
+ x[ci*_window_sz+xk]=window[xk]
+ *_in[(xi*_step+xk)*_nchannels+ci];
+ }
+ }
+ for(bi=xj=0;bi<_nbands;bi++){
+ float e2;
+ e2=0;
+ for(;xj<_bands[bi+1];xj++){
+ float p;
+ p=0;
+ for(ci=0;ci<_nchannels;ci++){
+ float re;
+ float im;
+ int ti;
+ ti=0;
+ re=im=0;
+ for(xk=0;xk<_window_sz;xk++){
+ re+=c[ti]*x[ci*_window_sz+xk];
+ im-=s[ti]*x[ci*_window_sz+xk];
+ ti+=xj;
+ if(ti>=_window_sz)ti-=_window_sz;
+ }
+ p+=OPUS_SQRTF(re*re+im*im);
+ }
+ p*=(1.0F/_nchannels);
+ e2+=p*p;
+ }
+ _out[xi*_nbands+bi]=e2/(_bands[bi+1]-_bands[bi])+1;
+ }
+ }
+ free(window);
+}
+
+static int cmp_float(const void *_a,const void *_b){
+ float a;
+ float b;
+ a=*(const float *)_a;
+ b=*(const float *)_b;
+ return (a>b)-(a<b);
+}
+
+#define NBANDS (21)
+
+/*Bands on which we compute the pseudo-NMR (Bark-derived
+ CELT bands).*/
+static const int BANDS[NBANDS+1]={
+ 0,2,4,6,8,10,12,14,16,20,24,28,32,40,48,56,68,80,96,120,156,200
+};
+
+/*Per-band NMR threshold.*/
+static const float NMR_THRESH[NBANDS]={
+85113.804F,72443.596F,61659.5F, 52480.746F,44668.359F,38018.940F,
+32359.366F,27542.287F,23442.288F,19952.623F,16982.437F,14454.398F,
+12302.688F,10471.285F, 8912.5094F,7585.7758F,6456.5423F,5495.4087F,
+ 4677.3514F,3981.0717F,3388.4416F
+};
+
+/*Noise floor.*/
+static const float NOISE_FLOOR[NBANDS]={
+8.7096359F,7.5857758F,6.6069345F,5.7543994F,5.0118723F,4.3651583F,
+3.8018940F,3.3113112F,2.8840315F,2.5118864F,2.1877616F,1.9054607F,
+1.6595869F,1.4454398F,1.2589254F,1.0964782F,0.95499259F,0.83176377F,
+0.72443596F,0.63095734F,0.54954087F
+};
+
+#define TEST_WIN_SIZE (480)
+#define TEST_WIN_STEP (TEST_WIN_SIZE>>1)
+
+int main(int _argc,const char **_argv){
+ FILE *fin1;
+ FILE *fin2;
+ float *x;
+ float *y;
+ float *xb;
+ float *eb;
+ float *nmr;
+ float thresh;
+ float mismatch;
+ float err;
+ float nmr_sum;
+ size_t weight;
+ size_t xlength;
+ size_t ylength;
+ size_t nframes;
+ size_t xi;
+ int bi;
+ int nchannels;
+ if(_argc<3||_argc>4){
+ fprintf(stderr,"Usage: %s [-s] <file1.sw> <file2.sw>\n",
+ _argv[0]);
+ return EXIT_FAILURE;
+ }
+ nchannels=1;
+ if(strcmp(_argv[1],"-s")==0)nchannels=2;
+ fin1=fopen(_argv[nchannels],"rb");
+ if(fin1==NULL){
+ fprintf(stderr,"Error opening '%s'.\n",_argv[nchannels]);
+ return EXIT_FAILURE;
+ }
+ fin2=fopen(_argv[nchannels+1],"rb");
+ if(fin2==NULL){
+ fprintf(stderr,"Error opening '%s'.\n",_argv[nchannels+1]);
+ fclose(fin1);
+ return EXIT_FAILURE;
+ }
+ /*Read in the data and allocate scratch space.*/
+ xlength=read_pcm16(&x,fin1,nchannels);
+ fclose(fin1);
+ ylength=read_pcm16(&y,fin2,nchannels);
+ fclose(fin2);
+ if(xlength!=ylength){
+ fprintf(stderr,"Sample counts do not match (%lu!=%lu).\n",
+ (unsigned long)xlength,(unsigned long)ylength);
+ return EXIT_FAILURE;
+ }
+ if(xlength<TEST_WIN_SIZE){
+ fprintf(stderr,"Insufficient sample data (%lu<%i).\n",
+ (unsigned long)xlength,TEST_WIN_SIZE);
+ return EXIT_FAILURE;
+ }
+ nframes=(xlength-TEST_WIN_SIZE+TEST_WIN_STEP)/TEST_WIN_STEP;
+ xb=(float *)opus_malloc(nframes*NBANDS*sizeof(*xb));
+ eb=(float *)opus_malloc(nframes*NBANDS*sizeof(*eb));
+ nmr=(float *)opus_malloc(nframes*NBANDS*sizeof(*nmr));
+ /*Compute the error signal.*/
+ for(xi=0;xi<xlength*nchannels;xi++){
+ err=x[xi]-y[xi];
+ y[xi]=err-OPUS_CLAMP(-1,err,1);
+ }
+ /*Compute the per-band spectral energy of the original signal
+ and the error.*/
+ band_energy(xb,BANDS,NBANDS,x,nchannels,nframes,
+ TEST_WIN_SIZE,TEST_WIN_STEP);
+ free(x);
+ band_energy(eb,BANDS,NBANDS,y,nchannels,nframes,
+ TEST_WIN_SIZE,TEST_WIN_STEP);
+ free(y);
+ nmr_sum=0;
+ for(xi=0;xi<nframes;xi++){
+ /*Frequency masking (low to high): 10 dB/Bark slope.*/
+ for(bi=1;bi<NBANDS;bi++)
+ xb[xi*NBANDS+bi]+=0.1F*xb[xi*NBANDS+bi-1];
+ /*Frequency masking (high to low): 15 dB/Bark slope.*/
+ for(bi=NBANDS-1;bi-->0;)
+ xb[xi*NBANDS+bi]+=0.03F*xb[xi*NBANDS+bi+1];
+ if(xi>0){
+ /*Temporal masking: 5 dB/5ms slope.*/
+ for(bi=0;bi<NBANDS;bi++)
+ xb[xi*NBANDS+bi]+=0.3F*xb[(xi-1)*NBANDS+bi];
+ }
+ /*Compute NMR.*/
+ for(bi=0;bi<NBANDS;bi++){
+ nmr[xi*NBANDS+bi]=xb[xi*NBANDS+bi]/eb[xi*NBANDS+bi];
+ nmr_sum+=10*OPUS_LOG10F(nmr[xi*NBANDS+bi]);
+ }
+ }
+ /*Find the 90th percentile of the errors.*/
+ memcpy(xb,eb,nframes*NBANDS*sizeof(*xb));
+ qsort(xb,nframes*NBANDS,sizeof(*xb),cmp_float);
+ thresh=xb[(9*nframes*NBANDS+5)/10];
+ free(xb);
+ /*Compute the mismatch.*/
+ mismatch=0;
+ weight=0;
+ for(xi=0;xi<nframes;xi++){
+ for(bi=0;bi<NBANDS;bi++){
+ if(eb[xi*NBANDS+bi]>thresh){
+ mismatch+=NMR_THRESH[bi]/nmr[xi*NBANDS+bi];
+ weight++;
+ }
+ }
+ }
+ free(nmr);
+ free(eb);
+ printf("Average pseudo-NMR: %3.2f dB\n",nmr_sum/(nframes*NBANDS));
+ if(weight<=0){
+ err=-100;
+ printf("Mismatch level: below noise floor\n");
+ }
+ else{
+ err=10*OPUS_LOG10F(mismatch/weight);
+ printf("Weighted mismatch: %3.2f dB\n",err);
+ }
+ printf("\n");
+ if(err<0){
+ printf("**Decoder PASSES test (mismatch < 0 dB)\n");
+ return EXIT_SUCCESS;
+ }
+ printf("**Decoder FAILS test (mismatch >= 0 dB)\n");
+ return EXIT_FAILURE;
+}