summaryrefslogtreecommitdiff
path: root/drivers/opus/opus_compare.c
diff options
context:
space:
mode:
authorAnton Yabchinskiy <arn@bestmx.ru>2015-11-02 20:25:01 +0300
committerAnton Yabchinskiy <arn@bestmx.ru>2015-11-02 20:25:01 +0300
commit3b9868d2e44740c03861c64020a8b5d4d6da031d (patch)
tree8ff5f9671122f946487848ce286d336c9b650c2c /drivers/opus/opus_compare.c
parentdc8df8a91a995796f0f330bf6bb6b209f6dfce08 (diff)
parentb2f9acb8c96aed0505cbac21661e21e4acef710f (diff)
Merge branch 'master' of github.com:okamstudio/godot
Diffstat (limited to 'drivers/opus/opus_compare.c')
-rw-r--r--drivers/opus/opus_compare.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/drivers/opus/opus_compare.c b/drivers/opus/opus_compare.c
new file mode 100644
index 0000000000..06c67d752f
--- /dev/null
+++ b/drivers/opus/opus_compare.c
@@ -0,0 +1,379 @@
+/* Copyright (c) 2011-2012 Xiph.Org Foundation, Mozilla Corporation
+ Written by Jean-Marc Valin and Timothy B. Terriberry */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define OPUS_PI (3.14159265F)
+
+#define OPUS_COSF(_x) ((float)cos(_x))
+#define OPUS_SINF(_x) ((float)sin(_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,float *_ps,const int *_bands,int _nbands,
+ const float *_in,int _nchannels,size_t _nframes,int _window_sz,
+ int _step,int _downsample){
+ float *window;
+ float *x;
+ float *c;
+ float *s;
+ size_t xi;
+ int xj;
+ int ps_sz;
+ window=(float *)opus_malloc((3+_nchannels)*_window_sz*sizeof(*window));
+ c=window+_window_sz;
+ s=c+_window_sz;
+ x=s+_window_sz;
+ ps_sz=_window_sz/2;
+ 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 p[2]={0};
+ for(;xj<_bands[bi+1];xj++){
+ 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;
+ }
+ re*=_downsample;
+ im*=_downsample;
+ _ps[(xi*ps_sz+xj)*_nchannels+ci]=re*re+im*im+100000;
+ p[ci]+=_ps[(xi*ps_sz+xj)*_nchannels+ci];
+ }
+ }
+ if(_out){
+ _out[(xi*_nbands+bi)*_nchannels]=p[0]/(_bands[bi+1]-_bands[bi]);
+ if(_nchannels==2){
+ _out[(xi*_nbands+bi)*_nchannels+1]=p[1]/(_bands[bi+1]-_bands[bi]);
+ }
+ }
+ }
+ }
+ free(window);
+}
+
+#define NBANDS (21)
+#define NFREQS (240)
+
+/*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
+};
+
+#define TEST_WIN_SIZE (480)
+#define TEST_WIN_STEP (120)
+
+int main(int _argc,const char **_argv){
+ FILE *fin1;
+ FILE *fin2;
+ float *x;
+ float *y;
+ float *xb;
+ float *X;
+ float *Y;
+ double err;
+ float Q;
+ size_t xlength;
+ size_t ylength;
+ size_t nframes;
+ size_t xi;
+ int ci;
+ int xj;
+ int bi;
+ int nchannels;
+ unsigned rate;
+ int downsample;
+ int ybands;
+ int yfreqs;
+ int max_compare;
+ if(_argc<3||_argc>6){
+ fprintf(stderr,"Usage: %s [-s] [-r rate2] <file1.sw> <file2.sw>\n",
+ _argv[0]);
+ return EXIT_FAILURE;
+ }
+ nchannels=1;
+ if(strcmp(_argv[1],"-s")==0){
+ nchannels=2;
+ _argv++;
+ }
+ rate=48000;
+ ybands=NBANDS;
+ yfreqs=NFREQS;
+ downsample=1;
+ if(strcmp(_argv[1],"-r")==0){
+ rate=atoi(_argv[2]);
+ if(rate!=8000&&rate!=12000&&rate!=16000&&rate!=24000&&rate!=48000){
+ fprintf(stderr,
+ "Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n");
+ return EXIT_FAILURE;
+ }
+ downsample=48000/rate;
+ switch(rate){
+ case 8000:ybands=13;break;
+ case 12000:ybands=15;break;
+ case 16000:ybands=17;break;
+ case 24000:ybands=19;break;
+ }
+ yfreqs=NFREQS/downsample;
+ _argv+=2;
+ }
+ fin1=fopen(_argv[1],"rb");
+ if(fin1==NULL){
+ fprintf(stderr,"Error opening '%s'.\n",_argv[1]);
+ return EXIT_FAILURE;
+ }
+ fin2=fopen(_argv[2],"rb");
+ if(fin2==NULL){
+ fprintf(stderr,"Error opening '%s'.\n",_argv[2]);
+ fclose(fin1);
+ return EXIT_FAILURE;
+ }
+ /*Read in the data and allocate scratch space.*/
+ xlength=read_pcm16(&x,fin1,2);
+ if(nchannels==1){
+ for(xi=0;xi<xlength;xi++)x[xi]=.5*(x[2*xi]+x[2*xi+1]);
+ }
+ fclose(fin1);
+ ylength=read_pcm16(&y,fin2,nchannels);
+ fclose(fin2);
+ if(xlength!=ylength*downsample){
+ fprintf(stderr,"Sample counts do not match (%lu!=%lu).\n",
+ (unsigned long)xlength,(unsigned long)ylength*downsample);
+ 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*nchannels*sizeof(*xb));
+ X=(float *)opus_malloc(nframes*NFREQS*nchannels*sizeof(*X));
+ Y=(float *)opus_malloc(nframes*yfreqs*nchannels*sizeof(*Y));
+ /*Compute the per-band spectral energy of the original signal
+ and the error.*/
+ band_energy(xb,X,BANDS,NBANDS,x,nchannels,nframes,
+ TEST_WIN_SIZE,TEST_WIN_STEP,1);
+ free(x);
+ band_energy(NULL,Y,BANDS,ybands,y,nchannels,nframes,
+ TEST_WIN_SIZE/downsample,TEST_WIN_STEP/downsample,downsample);
+ free(y);
+ for(xi=0;xi<nframes;xi++){
+ /*Frequency masking (low to high): 10 dB/Bark slope.*/
+ for(bi=1;bi<NBANDS;bi++){
+ for(ci=0;ci<nchannels;ci++){
+ xb[(xi*NBANDS+bi)*nchannels+ci]+=
+ 0.1F*xb[(xi*NBANDS+bi-1)*nchannels+ci];
+ }
+ }
+ /*Frequency masking (high to low): 15 dB/Bark slope.*/
+ for(bi=NBANDS-1;bi-->0;){
+ for(ci=0;ci<nchannels;ci++){
+ xb[(xi*NBANDS+bi)*nchannels+ci]+=
+ 0.03F*xb[(xi*NBANDS+bi+1)*nchannels+ci];
+ }
+ }
+ if(xi>0){
+ /*Temporal masking: -3 dB/2.5ms slope.*/
+ for(bi=0;bi<NBANDS;bi++){
+ for(ci=0;ci<nchannels;ci++){
+ xb[(xi*NBANDS+bi)*nchannels+ci]+=
+ 0.5F*xb[((xi-1)*NBANDS+bi)*nchannels+ci];
+ }
+ }
+ }
+ /* Allowing some cross-talk */
+ if(nchannels==2){
+ for(bi=0;bi<NBANDS;bi++){
+ float l,r;
+ l=xb[(xi*NBANDS+bi)*nchannels+0];
+ r=xb[(xi*NBANDS+bi)*nchannels+1];
+ xb[(xi*NBANDS+bi)*nchannels+0]+=0.01F*r;
+ xb[(xi*NBANDS+bi)*nchannels+1]+=0.01F*l;
+ }
+ }
+
+ /* Apply masking */
+ for(bi=0;bi<ybands;bi++){
+ for(xj=BANDS[bi];xj<BANDS[bi+1];xj++){
+ for(ci=0;ci<nchannels;ci++){
+ X[(xi*NFREQS+xj)*nchannels+ci]+=
+ 0.1F*xb[(xi*NBANDS+bi)*nchannels+ci];
+ Y[(xi*yfreqs+xj)*nchannels+ci]+=
+ 0.1F*xb[(xi*NBANDS+bi)*nchannels+ci];
+ }
+ }
+ }
+ }
+
+ /* Average of consecutive frames to make comparison slightly less sensitive */
+ for(bi=0;bi<ybands;bi++){
+ for(xj=BANDS[bi];xj<BANDS[bi+1];xj++){
+ for(ci=0;ci<nchannels;ci++){
+ float xtmp;
+ float ytmp;
+ xtmp = X[xj*nchannels+ci];
+ ytmp = Y[xj*nchannels+ci];
+ for(xi=1;xi<nframes;xi++){
+ float xtmp2;
+ float ytmp2;
+ xtmp2 = X[(xi*NFREQS+xj)*nchannels+ci];
+ ytmp2 = Y[(xi*yfreqs+xj)*nchannels+ci];
+ X[(xi*NFREQS+xj)*nchannels+ci] += xtmp;
+ Y[(xi*yfreqs+xj)*nchannels+ci] += ytmp;
+ xtmp = xtmp2;
+ ytmp = ytmp2;
+ }
+ }
+ }
+ }
+
+ /*If working at a lower sampling rate, don't take into account the last
+ 300 Hz to allow for different transition bands.
+ For 12 kHz, we don't skip anything, because the last band already skips
+ 400 Hz.*/
+ if(rate==48000)max_compare=BANDS[NBANDS];
+ else if(rate==12000)max_compare=BANDS[ybands];
+ else max_compare=BANDS[ybands]-3;
+ err=0;
+ for(xi=0;xi<nframes;xi++){
+ double Ef;
+ Ef=0;
+ for(bi=0;bi<ybands;bi++){
+ double Eb;
+ Eb=0;
+ for(xj=BANDS[bi];xj<BANDS[bi+1]&&xj<max_compare;xj++){
+ for(ci=0;ci<nchannels;ci++){
+ float re;
+ float im;
+ re=Y[(xi*yfreqs+xj)*nchannels+ci]/X[(xi*NFREQS+xj)*nchannels+ci];
+ im=re-log(re)-1;
+ /*Make comparison less sensitive around the SILK/CELT cross-over to
+ allow for mode freedom in the filters.*/
+ if(xj>=79&&xj<=81)im*=0.1F;
+ if(xj==80)im*=0.1F;
+ Eb+=im;
+ }
+ }
+ Eb /= (BANDS[bi+1]-BANDS[bi])*nchannels;
+ Ef += Eb*Eb;
+ }
+ /*Using a fixed normalization value means we're willing to accept slightly
+ lower quality for lower sampling rates.*/
+ Ef/=NBANDS;
+ Ef*=Ef;
+ err+=Ef*Ef;
+ }
+ err=pow(err/nframes,1.0/16);
+ Q=100*(1-0.5*log(1+err)/log(1.13));
+ if(Q<0){
+ fprintf(stderr,"Test vector FAILS\n");
+ fprintf(stderr,"Internal weighted error is %f\n",err);
+ return EXIT_FAILURE;
+ }
+ else{
+ fprintf(stderr,"Test vector PASSES\n");
+ fprintf(stderr,
+ "Opus quality metric: %.1f %% (internal weighted error is %f)\n",Q,err);
+ return EXIT_SUCCESS;
+ }
+}