/*
* Copyright (C) 2010 Preston Lacey http://javaflacencoder.sourceforge.net/
* All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package javaFlacEncoder;
/**
* This class is used to calculate LPC Coefficients for a FLAC stream.
*
* @author Preston Lacey
*/
public class LPC {
/** The error calculated by the LPC algorithm */
protected double rawError;
/** The coefficients as calculated by the LPC algorithm */
protected double[] rawCoefficients;
/** The order of this LPC calculation */
protected int order;
/**
* Constructor creates an LPC object of the given order.
* @param order Order for this LPC calculation.
*/
public LPC(int order) {
this.order = order;
rawError = 0;
rawCoefficients = null;
}
/**
* Get this LPC object's order
* @return order used for this LPC calculation.
*/
public int getOrder () { return order; }
/**
* Get the error for this LPC calculation
* @return lpc error
*/
public double getError() { return rawError; }
/**
* Get the calculated LPC Coefficients as an array.
* @return lpc coefficients in an array.
*/
public double[] getCoefficients() { return rawCoefficients; }
/**
* Calculate an LPC using the given Auto-correlation data. Static method
* used since this is slightly faster than a more strictly object-oriented
* approach.
*
* @param lpc LPC to calculate
* @param R Autocorrelation data to use
*/
public static void calculate(LPC lpc, long[] R) {
//calculate R(autocorrelation coefficients)
int coeffCount = lpc.order;
//calculate first iteration directly
double[] A = new double[coeffCount+1];
A[0] = 1;
double E = R[0];
//calculate remaining iterations
if(R[0] == 0) {
for(int i = 0; i < coeffCount+1; i++)
A[i] = 0.0;
}
else {
double[] ATemp = new double[coeffCount+1];
for(int k = 0; k < coeffCount; k++) {
double lambda = 0.0;
double temp = 0;
for(int j = 0; j <= k; j++) {
temp += A[j]*R[k+1-j];
}
lambda = -temp/E;
//lambda = temp;
for(int i = 0; i <= k+1; i++) {//verified good?
ATemp[i] = A[i]+lambda*A[k+1-i];
}
System.arraycopy(ATemp, 0, A, 0, coeffCount+1);
E = (1-lambda*lambda)*E;
}
}
lpc.rawCoefficients = A;
lpc.rawError = E;
}
/**
* Calculate an LPC using a prior order LPC's values to save calculations.
*
* @param lpc LPC to calculate
* @param R Auto-correlation data to use.
* @param priorLPC Prior order LPC to use(may be any order lower than our
* target LPC)
*
*/
public static void calculateFromPrior(LPC lpc, long[] R, LPC priorLPC) {
//calculate R(autocorrelation coefficients)
int coeffCount = lpc.order;
//calculate first iteration directly
double[] A = new double[coeffCount+1];
A[0] = 1;
//A[1] = -R[1]/R[0];
//double E = R[0]+R[1]*A[1];
double E = R[0];
int startIter = 0;
if(priorLPC != null && priorLPC.order < lpc.order) {
startIter = priorLPC.order;
E = priorLPC.rawError;
System.arraycopy(priorLPC.rawCoefficients, 0, A, 0, startIter+1);
}
//calculate remaining iterations
if(R[0] == 0) {
for(int i = 0; i < coeffCount+1; i++)
A[i] = 0.0;
}
else {
double[] ATemp = new double[coeffCount+1];
for(int k = startIter; k < coeffCount; k++) {
double lambda = 0.0;
double temp = 0.0;
for(int j = 0; j <= k; j++) {
temp -= A[j]*R[k-j+1];
}
lambda = temp/E;
for(int i = 0; i <= k+1; i++) {
ATemp[i] = A[i]+lambda*A[k+1-i];
}
System.arraycopy(ATemp, 0, A, 0, coeffCount+1);
E = (1-lambda*lambda)*E;
}
}
lpc.rawCoefficients = A;
lpc.rawError = E;
}
/**
* Create auto-correlation coefficients(up to a maxOrder of 32).
* @param R Array to put results in.
* @param samples Samples to calculate the auto-correlation for.
* @param count number of samples to use
* @param start index of samples array to start at
* @param increment number of indices to increment between valid samples(for
* interleaved arrays)
* @param maxOrder maximum order to calculate.
*/
public static void createAutoCorrelation(long[] R, int []samples, int count,
int start, int increment, int maxOrder) {
//samples = window(samples,count,start,increment);
//start = 0;
//increment = 1;
if(increment == 1) {
for(int i = 0; i <= maxOrder; i++) {
R[i] = 0;
long temp = 0;
int baseIndex = i;
for(int j = 0; j < count-i; j++) {
temp += samples[j]*samples[j+baseIndex];
}
R[i] += temp;
}
}
else {
for(int i = 0; i <= maxOrder; i++) {
R[i] = 0;
int baseIndex = increment*i;
long temp = 0;
int innerLimit = (count-i)*increment;
for(int j = start; j < innerLimit; j+=increment) {
temp += samples[j]*samples[j+baseIndex];
}
R[i] += temp;
}
}
}
/**
* Apply a window function to sample data
* @param samples Samples to apply window to. Values in this array are left
* unaltered.
* @param count number of samples to use
* @param start index of samples array to start at
* @param increment number of indices to increment between valid samples(for
* interleaved arrays)
* @param windowedSamples array containing windowed values. Return values
* are packed(increment of one).
*
*/
public static void window(int[] samples, int count, int start, int increment,
int[] windowedSamples) {
//int[] values = new int[samples.length];
//int[] values = new int[count];
int[] values = windowedSamples;
int loopCount = 0;
float halfway = count/2.0f;
float windowCount = -halfway;
int limit = count*increment+start;
for(int i = start; i < limit; i+=increment) {
float innerCount = (windowCount < 0) ? -windowCount:windowCount;
windowCount++;
//double val = 1.0-(double)(innerCount/halfway);
float val = 1.0f-( (innerCount*innerCount)/(halfway*halfway) );
double temp = ((double)samples[i])*val;
temp = (temp >0) ? temp+0.5:temp-0.5;
values[loopCount++] = (int)temp;
//values[i] = (int)(((double)samples[i])*val);
}
}
}