/* JOrbis * Copyright (C) 2000 ymnk, JCraft,Inc. * * Written by: 2000 ymnk<ymnk@jcraft.com> * * Many thanks to * Monty <monty@xiph.org> and * The XIPHOPHORUS Company http://www.xiph.org/ . * JOrbis has been based on their awesome works, Vorbis codec. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library 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 Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.jcraft.jorbis; class Lpc{ // en/decode lookups Drft fft=new Drft();; int ln; int m; // Autocorrelation LPC coeff generation algorithm invented by // N. Levinson in 1947, modified by J. Durbin in 1959. // Input : n elements of time doamin data // Output: m lpc coefficients, excitation energy static float lpc_from_data(float[] data, float[] lpc,int n,int m){ float[] aut=new float[m+1]; float error; int i,j; // autocorrelation, p+1 lag coefficients j=m+1; while(j--!=0){ float d=0; for(i=j;i<n;i++)d+=data[i]*data[i-j]; aut[j]=d; } // Generate lpc coefficients from autocorr values error=aut[0]; /* if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; } */ for(i=0;i<m;i++){ float r=-aut[i+1]; if(error==0){ for(int k=0; k<m; k++) lpc[k]=0.0f; return 0; } // Sum up this iteration's reflection coefficient; note that in // Vorbis we don't save it. If anyone wants to recycle this code // and needs reflection coefficients, save the results of 'r' from // each iteration. for(j=0;j<i;j++)r-=lpc[j]*aut[i-j]; r/=error; // Update LPC coefficients and total error lpc[i]=r; for(j=0;j<i/2;j++){ float tmp=lpc[j]; lpc[j]+=r*lpc[i-1-j]; lpc[i-1-j]+=r*tmp; } if(i%2!=0)lpc[j]+=lpc[j]*r; error*=1.0-r*r; } // we need the error value to know how big an impulse to hit the // filter with later return error; } // Input : n element envelope spectral curve // Output: m lpc coefficients, excitation energy float lpc_from_curve(float[] curve, float[] lpc){ int n=ln; float[] work=new float[n+n]; float fscale=(float)(.5/n); int i,j; // input is a real curve. make it complex-real // This mixes phase, but the LPC generation doesn't care. for(i=0;i<n;i++){ work[i*2]=curve[i]*fscale; work[i*2+1]=0; } work[n*2-1]=curve[n-1]*fscale; n*=2; fft.backward(work); // The autocorrelation will not be circular. Shift, else we lose // most of the power in the edges. for(i=0,j=n/2;i<n/2;){ float temp=work[i]; work[i++]=work[j]; work[j++]=temp; } return(lpc_from_data(work,lpc,n,m)); } void init(int mapped, int m){ //memset(l,0,sizeof(lpc_lookup)); ln=mapped; this.m=m; // we cheat decoding the LPC spectrum via FFTs fft.init(mapped*2); } void clear(){ fft.clear(); } static float FAST_HYPOT(float a, float b){ return (float)Math.sqrt((a)*(a) + (b)*(b)); } // One can do this the long way by generating the transfer function in // the time domain and taking the forward FFT of the result. The // results from direct calculation are cleaner and faster. // // This version does a linear curve generation and then later // interpolates the log curve from the linear curve. void lpc_to_curve(float[] curve, float[] lpc, float amp){ //memset(curve,0,sizeof(float)*l->ln*2); for(int i=0; i<ln*2; i++)curve[i]=0.0f; if(amp==0)return; for(int i=0;i<m;i++){ curve[i*2+1]=lpc[i]/(4*amp); curve[i*2+2]=-lpc[i]/(4*amp); } fft.backward(curve); // reappropriated ;-) { int l2=ln*2; float unit=(float)(1./amp); curve[0]=(float)(1./(curve[0]*2+unit)); for(int i=1;i<ln;i++){ float real=(curve[i]+curve[l2-i]); float imag=(curve[i]-curve[l2-i]); float a = real + unit; curve[i] = (float)(1.0 / FAST_HYPOT(a, imag)); } } } /* // subtract or add an lpc filter to data. Vorbis doesn't actually use this. static void lpc_residue(float[] coeff, float[] prime,int m, float[] data, int n){ // in: coeff[0...m-1] LPC coefficients // prime[0...m-1] initial values // data[0...n-1] data samples // out: data[0...n-1] residuals from LPC prediction float[] work=new float[m+n]; float y; if(prime==null){ for(int i=0;i<m;i++){ work[i]=0; } } else{ for(int i=0;i<m;i++){ work[i]=prime[i]; } } for(int i=0;i<n;i++){ y=0; for(int j=0;j<m;j++){ y-=work[i+j]*coeff[m-j-1]; } work[i+m]=data[i]; data[i]-=y; } } static void lpc_predict(float[] coeff, float[] prime,int m, float[] data, int n){ // in: coeff[0...m-1] LPC coefficients // prime[0...m-1] initial values (allocated size of n+m-1) // data[0...n-1] residuals from LPC prediction // out: data[0...n-1] data samples int o,p; float y; float[] work=new float[m+n]; if(prime==null){ for(int i=0;i<m;i++){ work[i]=0.f; } } else{ for(int i=0;i<m;i++){ work[i]=prime[i]; } } for(int i=0;i<n;i++){ y=data[i]; o=i; p=m; for(int j=0;j<m;j++){ y-=work[o++]*coeff[--p]; } data[i]=work[o]=y; } } */ }