diff options
author | Anton Yabchinskiy <arn@bestmx.ru> | 2015-11-02 20:25:01 +0300 |
---|---|---|
committer | Anton Yabchinskiy <arn@bestmx.ru> | 2015-11-02 20:25:01 +0300 |
commit | 3b9868d2e44740c03861c64020a8b5d4d6da031d (patch) | |
tree | 8ff5f9671122f946487848ce286d336c9b650c2c /drivers/opus/opus_compare.c | |
parent | dc8df8a91a995796f0f330bf6bb6b209f6dfce08 (diff) | |
parent | b2f9acb8c96aed0505cbac21661e21e4acef710f (diff) |
Merge branch 'master' of github.com:okamstudio/godot
Diffstat (limited to 'drivers/opus/opus_compare.c')
-rw-r--r-- | drivers/opus/opus_compare.c | 379 |
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; + } +} |