123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- /* Copyright (C) 2005 Jean-Marc Valin, CSIRO, Christopher Montgomery
- File: vorbis_psy.c
- 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.
- - Neither the name of the Xiph.org Foundation nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
- 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 FOUNDATION 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.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #ifdef VORBIS_PSYCHO
- #include "arch.h"
- #include "smallft.h"
- #include "lpc.h"
- #include "vorbis_psy.h"
- #include "os_support.h"
- #include <stdlib.h>
- #include <math.h>
- #include <string.h>
- /* psychoacoustic setup ********************************************/
- static VorbisPsyInfo example_tuning = {
- .5,.5,
- 3,3,25,
- /*63 125 250 500 1k 2k 4k 8k 16k*/
- // vorbis mode 4 style
- //{-32,-32,-32,-32,-28,-24,-22,-20,-20, -20, -20, -8, -6, -6, -6, -6, -6},
- { -4, -6, -6, -6, -6, -6, -6, -6, -8, -8,-10,-10, -8, -6, -4, -4, -2},
- {
- 0, 1, 2, 3, 4, 5, 5, 5, /* 7dB */
- 6, 6, 6, 5, 4, 4, 4, 4, /* 15dB */
- 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */
- 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */
- 11,12,13,14,15,16,17, 18, /* 39dB */
- }
- };
- /* there was no great place to put this.... */
- #include <stdio.h>
- static void _analysis_output(char *base,int i,float *v,int n,int bark,int dB){
- int j;
- FILE *of;
- char buffer[80];
- sprintf(buffer,"%s_%d.m",base,i);
- of=fopen(buffer,"w");
- if(!of)perror("failed to open data dump file");
- for(j=0;j<n;j++){
- if(bark){
- float b=toBARK((4000.f*j/n)+.25);
- fprintf(of,"%f ",b);
- }else
- fprintf(of,"%f ",(double)j);
- if(dB){
- float val;
- if(v[j]==0.)
- val=-140.;
- else
- val=todB(v[j]);
- fprintf(of,"%f\n",val);
- }else{
- fprintf(of,"%f\n",v[j]);
- }
- }
- fclose(of);
- }
- static void bark_noise_hybridmp(int n,const long *b,
- const float *f,
- float *noise,
- const float offset,
- const int fixed){
- float *N=alloca(n*sizeof(*N));
- float *X=alloca(n*sizeof(*N));
- float *XX=alloca(n*sizeof(*N));
- float *Y=alloca(n*sizeof(*N));
- float *XY=alloca(n*sizeof(*N));
- float tN, tX, tXX, tY, tXY;
- int i;
- int lo, hi;
- float R, A, B, D;
- float w, x, y;
- tN = tX = tXX = tY = tXY = 0.f;
- y = f[0] + offset;
- if (y < 1.f) y = 1.f;
- w = y * y * .5;
- tN += w;
- tX += w;
- tY += w * y;
- N[0] = tN;
- X[0] = tX;
- XX[0] = tXX;
- Y[0] = tY;
- XY[0] = tXY;
- for (i = 1, x = 1.f; i < n; i++, x += 1.f) {
- y = f[i] + offset;
- if (y < 1.f) y = 1.f;
- w = y * y;
- tN += w;
- tX += w * x;
- tXX += w * x * x;
- tY += w * y;
- tXY += w * x * y;
- N[i] = tN;
- X[i] = tX;
- XX[i] = tXX;
- Y[i] = tY;
- XY[i] = tXY;
- }
- for (i = 0, x = 0.f;; i++, x += 1.f) {
- lo = b[i] >> 16;
- if( lo>=0 ) break;
- hi = b[i] & 0xffff;
- tN = N[hi] + N[-lo];
- tX = X[hi] - X[-lo];
- tXX = XX[hi] + XX[-lo];
- tY = Y[hi] + Y[-lo];
- tXY = XY[hi] - XY[-lo];
- A = tY * tXX - tX * tXY;
- B = tN * tXY - tX * tY;
- D = tN * tXX - tX * tX;
- R = (A + x * B) / D;
- if (R < 0.f)
- R = 0.f;
- noise[i] = R - offset;
- }
- for ( ;; i++, x += 1.f) {
- lo = b[i] >> 16;
- hi = b[i] & 0xffff;
- if(hi>=n)break;
- tN = N[hi] - N[lo];
- tX = X[hi] - X[lo];
- tXX = XX[hi] - XX[lo];
- tY = Y[hi] - Y[lo];
- tXY = XY[hi] - XY[lo];
- A = tY * tXX - tX * tXY;
- B = tN * tXY - tX * tY;
- D = tN * tXX - tX * tX;
- R = (A + x * B) / D;
- if (R < 0.f) R = 0.f;
- noise[i] = R - offset;
- }
- for ( ; i < n; i++, x += 1.f) {
- R = (A + x * B) / D;
- if (R < 0.f) R = 0.f;
- noise[i] = R - offset;
- }
- if (fixed <= 0) return;
- for (i = 0, x = 0.f;; i++, x += 1.f) {
- hi = i + fixed / 2;
- lo = hi - fixed;
- if(lo>=0)break;
- tN = N[hi] + N[-lo];
- tX = X[hi] - X[-lo];
- tXX = XX[hi] + XX[-lo];
- tY = Y[hi] + Y[-lo];
- tXY = XY[hi] - XY[-lo];
- A = tY * tXX - tX * tXY;
- B = tN * tXY - tX * tY;
- D = tN * tXX - tX * tX;
- R = (A + x * B) / D;
- if (R - offset < noise[i]) noise[i] = R - offset;
- }
- for ( ;; i++, x += 1.f) {
- hi = i + fixed / 2;
- lo = hi - fixed;
- if(hi>=n)break;
- tN = N[hi] - N[lo];
- tX = X[hi] - X[lo];
- tXX = XX[hi] - XX[lo];
- tY = Y[hi] - Y[lo];
- tXY = XY[hi] - XY[lo];
- A = tY * tXX - tX * tXY;
- B = tN * tXY - tX * tY;
- D = tN * tXX - tX * tX;
- R = (A + x * B) / D;
- if (R - offset < noise[i]) noise[i] = R - offset;
- }
- for ( ; i < n; i++, x += 1.f) {
- R = (A + x * B) / D;
- if (R - offset < noise[i]) noise[i] = R - offset;
- }
- }
- static void _vp_noisemask(VorbisPsy *p,
- float *logfreq,
- float *logmask){
- int i,n=p->n/2;
- float *work=alloca(n*sizeof(*work));
- bark_noise_hybridmp(n,p->bark,logfreq,logmask,
- 140.,-1);
- for(i=0;i<n;i++)work[i]=logfreq[i]-logmask[i];
- bark_noise_hybridmp(n,p->bark,work,logmask,0.,
- p->vi->noisewindowfixed);
- for(i=0;i<n;i++)work[i]=logfreq[i]-work[i];
- {
- static int seq=0;
- float work2[n];
- for(i=0;i<n;i++){
- work2[i]=logmask[i]+work[i];
- }
- //_analysis_output("logfreq",seq,logfreq,n,0,0);
- //_analysis_output("median",seq,work,n,0,0);
- //_analysis_output("envelope",seq,work2,n,0,0);
- seq++;
- }
- for(i=0;i<n;i++){
- int dB=logmask[i]+.5;
- if(dB>=NOISE_COMPAND_LEVELS)dB=NOISE_COMPAND_LEVELS-1;
- if(dB<0)dB=0;
- logmask[i]= work[i]+p->vi->noisecompand[dB]+p->noiseoffset[i];
- }
- }
- VorbisPsy *vorbis_psy_init(int rate, int n)
- {
- long i,j,lo=-99,hi=1;
- VorbisPsy *p = speex_alloc(sizeof(VorbisPsy));
- memset(p,0,sizeof(*p));
- p->n = n;
- spx_drft_init(&p->lookup, n);
- p->bark = speex_alloc(n*sizeof(*p->bark));
- p->rate=rate;
- p->vi = &example_tuning;
- /* BH4 window */
- p->window = speex_alloc(sizeof(*p->window)*n);
- float a0 = .35875f;
- float a1 = .48829f;
- float a2 = .14128f;
- float a3 = .01168f;
- for(i=0;i<n;i++)
- p->window[i] = //a0 - a1*cos(2.*M_PI/n*(i+.5)) + a2*cos(4.*M_PI/n*(i+.5)) - a3*cos(6.*M_PI/n*(i+.5));
- sin((i+.5)/n * M_PI)*sin((i+.5)/n * M_PI);
- /* bark scale lookups */
- for(i=0;i<n;i++){
- float bark=toBARK(rate/(2*n)*i);
- for(;lo+p->vi->noisewindowlomin<i &&
- toBARK(rate/(2*n)*lo)<(bark-p->vi->noisewindowlo);lo++);
- for(;hi<=n && (hi<i+p->vi->noisewindowhimin ||
- toBARK(rate/(2*n)*hi)<(bark+p->vi->noisewindowhi));hi++);
- p->bark[i]=((lo-1)<<16)+(hi-1);
- }
- /* set up rolling noise median */
- p->noiseoffset=speex_alloc(n*sizeof(*p->noiseoffset));
- for(i=0;i<n;i++){
- float halfoc=toOC((i+.5)*rate/(2.*n))*2.;
- int inthalfoc;
- float del;
- if(halfoc<0)halfoc=0;
- if(halfoc>=P_BANDS-1)halfoc=P_BANDS-1;
- inthalfoc=(int)halfoc;
- del=halfoc-inthalfoc;
- p->noiseoffset[i]=
- p->vi->noiseoff[inthalfoc]*(1.-del) +
- p->vi->noiseoff[inthalfoc+1]*del;
- }
- #if 0
- _analysis_output_always("noiseoff0",ls,p->noiseoffset,n,1,0,0);
- #endif
- return p;
- }
- void vorbis_psy_destroy(VorbisPsy *p)
- {
- if(p){
- spx_drft_clear(&p->lookup);
- if(p->bark)
- speex_free(p->bark);
- if(p->noiseoffset)
- speex_free(p->noiseoffset);
- if(p->window)
- speex_free(p->window);
- memset(p,0,sizeof(*p));
- speex_free(p);
- }
- }
- void compute_curve(VorbisPsy *psy, float *audio, float *curve)
- {
- int i;
- float work[psy->n];
- float scale=4.f/psy->n;
- float scale_dB;
- scale_dB=todB(scale);
- /* window the PCM data; use a BH4 window, not vorbis */
- for(i=0;i<psy->n;i++)
- work[i]=audio[i] * psy->window[i];
- {
- static int seq=0;
- //_analysis_output("win",seq,work,psy->n,0,0);
- seq++;
- }
- /* FFT yields more accurate tonal estimation (not phase sensitive) */
- spx_drft_forward(&psy->lookup,work);
- /* magnitudes */
- work[0]=scale_dB+todB(work[0]);
- for(i=1;i<psy->n-1;i+=2){
- float temp = work[i]*work[i] + work[i+1]*work[i+1];
- work[(i+1)>>1] = scale_dB+.5f * todB(temp);
- }
- /* derive a noise curve */
- _vp_noisemask(psy,work,curve);
- #define SIDEL 12
- for (i=0;i<SIDEL;i++)
- {
- curve[i]=curve[SIDEL];
- }
- #define SIDEH 12
- for (i=0;i<SIDEH;i++)
- {
- curve[(psy->n>>1)-i-1]=curve[(psy->n>>1)-SIDEH];
- }
- for(i=0;i<((psy->n)>>1);i++)
- curve[i] = fromdB(1.2*curve[i]+.2*i);
- //curve[i] = fromdB(0.8*curve[i]+.35*i);
- //curve[i] = fromdB(0.9*curve[i])*pow(1.0*i+45,1.3);
- }
- /* Transform a masking curve (power spectrum) into a pole-zero filter */
- void curve_to_lpc(VorbisPsy *psy, float *curve, float *awk1, float *awk2, int ord)
- {
- int i;
- float ac[psy->n];
- float tmp;
- int len = psy->n >> 1;
- for (i=0;i<2*len;i++)
- ac[i] = 0;
- for (i=1;i<len;i++)
- ac[2*i-1] = curve[i];
- ac[0] = curve[0];
- ac[2*len-1] = curve[len-1];
- spx_drft_backward(&psy->lookup, ac);
- _spx_lpc(awk1, ac, ord);
- tmp = 1.;
- for (i=0;i<ord;i++)
- {
- tmp *= .99;
- awk1[i] *= tmp;
- }
- #if 0
- for (i=0;i<ord;i++)
- awk2[i] = 0;
- #else
- /* Use the second (awk2) filter to correct the first one */
- for (i=0;i<2*len;i++)
- ac[i] = 0;
- for (i=0;i<ord;i++)
- ac[i+1] = awk1[i];
- ac[0] = 1;
- spx_drft_forward(&psy->lookup, ac);
- /* Compute (power) response of awk1 (all zero) */
- ac[0] *= ac[0];
- for (i=1;i<len;i++)
- ac[i] = ac[2*i-1]*ac[2*i-1] + ac[2*i]*ac[2*i];
- ac[len] = ac[2*len-1]*ac[2*len-1];
- /* Compute correction required */
- for (i=0;i<len;i++)
- curve[i] = 1. / (1e-6f+curve[i]*ac[i]);
- for (i=0;i<2*len;i++)
- ac[i] = 0;
- for (i=1;i<len;i++)
- ac[2*i-1] = curve[i];
- ac[0] = curve[0];
- ac[2*len-1] = curve[len-1];
- spx_drft_backward(&psy->lookup, ac);
- _spx_lpc(awk2, ac, ord);
- tmp = 1;
- for (i=0;i<ord;i++)
- {
- tmp *= .99;
- awk2[i] *= tmp;
- }
- #endif
- }
- #if 0
- #include <stdio.h>
- #include <math.h>
- #define ORDER 10
- #define CURVE_SIZE 24
- int main()
- {
- int i;
- float curve[CURVE_SIZE];
- float awk1[ORDER], awk2[ORDER];
- for (i=0;i<CURVE_SIZE;i++)
- scanf("%f ", &curve[i]);
- for (i=0;i<CURVE_SIZE;i++)
- curve[i] = pow(10.f, .1*curve[i]);
- curve_to_lpc(curve, CURVE_SIZE, awk1, awk2, ORDER);
- for (i=0;i<ORDER;i++)
- printf("%f ", awk1[i]);
- printf ("\n");
- for (i=0;i<ORDER;i++)
- printf("%f ", awk2[i]);
- printf ("\n");
- return 0;
- }
- #endif
- #endif
|