/*
* 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;
/**
* Implements the Subframe abstract class, providing encoding support for the
* FLAC Fixed-predictor Subframe.
*
* @author Preston Lacey
*/
public class Subframe_Fixed extends Subframe {
/** For debugging: Higher values equals greater output, generally in
* increments of 10 */
public static int DEBUG_LEV = 0;
/** Subframe type supported by this implementation. */
public static final EncodingConfiguration.SubframeType type =
EncodingConfiguration.SubframeType.FIXED;
int sampleSize = 0;
RiceEncoder rice = null;
int [] bits;
int [] lowOrderBits;
long [] sum;
int _error1[] = null;
int _error2[] = null;
int _error3[] = null;
int _error4[] = null;
int _lastCount = 0;
int _order;
int[] _errors = null;
int _offset = 0;
int _start = 0;
int _skip = 0;
int _errorStep = 0;
int _totalBits;
int[] _samples = null;
int _errorOffset = 0;
int _errorCount = 0;
int _frameSampleSize = 0;
private static final double LOG_2 = Math.log(2);
/**
* Constructor. Sets StreamConfiguration to use. If the StreamConfiguration
* must later be changed, a new Subframe object must be created as well.
*
* @param sc StreamConfiguration to use for encoding.
*/
public Subframe_Fixed(StreamConfiguration sc) {
super(sc);
sampleSize = sc.getBitsPerSample();
rice = new RiceEncoder();
bits = new int[5];
lowOrderBits = new int[5];
sum = new long[5];
_lastCount = -1;
}
/**
* This method is used to set the encoding configuration.
* @param ec encoding configuration to use.
* @return true if configuration was changed, false otherwise
*/
@Override
public boolean registerConfiguration(EncodingConfiguration ec) {
super.registerConfiguration(ec);
return true;
}
public int encodeSamples(int[] samples, int count, int start, int skip,
int offset, int unencSampleSize ) {
int encodedSamples = count;
if(DEBUG_LEV > 0) {
System.err.println("Subframe_Fixed::encodeSamples(...) : Begin");
if(DEBUG_LEV > 10) {
System.err.println("--count : " +count);
System.err.println("start:skip:offset:::"+start+":"+skip+":"+offset);
}
}
int increment = skip+1;
//create space for results: Need four sets for the 5 different versions,
// the e0 is sampe as input samples, so no duplicate needed.
if(count != _lastCount) {
_error1 = new int[count];
_error2 = new int[count];
_error3 = new int[count];
_error4 = new int[count];
_lastCount = count;
}
int [] error1 = _error1;
int [] error2 = _error2;
int [] error3 = _error3;
int [] error4 = _error4;
long sum0 = 0;
long sum1 = 0;
long sum2 = 0;
long sum3 = 0;
long sum4 = 0;
//apply the algorithm to determine errors, summing abs vals as we go
int tempI;
int index = start;
for(int i = 0; i < count; i++) {
tempI = samples[index];
if(tempI < 0) tempI = -tempI;
sum0 += tempI;
index += increment;
}
for(int i = 1; i < 5; i++) {
error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment];
tempI = error1[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum1 += tempI;
if(i > 1) {
error2[i] = error1[i]-error1[(i-1)];
tempI = error2[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum2 += tempI;
}
if(i > 2) {
error3[i] = error2[i]-error2[(i-1)];
tempI = error3[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum3 += tempI;
}
if(i > 3) {
error4[i] = error3[i]-error3[(i-1)];
tempI = error4[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum4 += tempI;
}
}
index = start+5*increment;
for(int i = 5; i < count; i++) {
//error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment];
error1[i] = samples[index]-samples[index-increment];
tempI = error1[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum1 += tempI;
error2[i] = error1[i]-error1[(i-1)];
tempI = error2[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum2 += tempI;
error3[i] = error2[i]-error2[(i-1)];
tempI = error3[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum3 += tempI;
error4[i] = error3[i]-error3[(i-1)];
tempI = error4[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum4 += tempI;
index += increment;
}
//select best algorithm as indicated by bits needed from sum of values
// and number of priming samples needed.
int order = 0;
long sumsX;
for(int i = 0; i < 5; i++) {
if(i == 0)
sumsX = sum0;
else if(i == 1)
sumsX = sum1;
else if(i == 2)
sumsX = sum2;
else if(i == 3)
sumsX = sum3;
else
sumsX = sum4;
double tempLowOrderBits = LOG_2*(sumsX/(count-i));
lowOrderBits[i] = (int)(Math.ceil(Math.log(tempLowOrderBits)/LOG_2));
if(lowOrderBits[i] < 1)
lowOrderBits[i] = 1;
else if (lowOrderBits[i] > sampleSize)
lowOrderBits[i] = sampleSize;
//lowOrderBits[i]++;//DOUBLE CHECK VALIDITY OF THIS. Decreases the bits needed, but "shouldn't"
//bits[i] = (int)(Math.log(sum[i])/Math.log(10))*(count-1)+sampleSize*i;
bits[i] = (int)(lowOrderBits[i]*(count-i)+sampleSize*i+1);
order = (bits[i] < bits[order]) ? i:order;
}
int[] errors = null;
int errorCount = count-order;
int errorOffset = order;
int errorStep = 1;
switch(order) {
case 0: errors = samples;
errorStep+=skip;
errorOffset=start;break;
case 1: errors = error1;break;
case 2: errors = error2;break;
case 3: errors = error3;break;
case 4: errors = error4;break;
}
_order = order;
_offset = offset;
_start = start;
_errorStep = errorStep;
_errorOffset = errorOffset;
_errorCount = errorCount;
_skip = skip;
_samples = samples;
_frameSampleSize = unencSampleSize;
_errors = errors;
_totalBits = unencSampleSize*order+8+ RiceEncoder.calculateEncodeSize(
errors,errorOffset, errorStep, errorCount, lowOrderBits[order]);
return encodedSamples;
}
/**
* Return the estimated size of the previous encode attempt in bits. Since
* returning the data from an encode is costly(due to the rice encoding and FLAC
* compliant bit-packing), this allows us to estimate the size first, and
* therefore choose another subframe type if this is larger.
*
* @return estimated size in bits of encoded subframe.
*/
public int estimatedSize() {
return _totalBits;
}
/**
* Get the data from the last encode attempt. Data is returned in an
* EncodedElement, properly packed at the bit-level to be added directly to
* a FLAC stream.
*
* @return EncodedElement containing encoded subframe
*/
public EncodedElement getData() {
EncodedElement dataEle = new EncodedElement();
dataEle.clear(_totalBits,_offset);
int unencSampleSize = _frameSampleSize;
//write headers
int encodedType = 1<<3 | _order;
dataEle.addInt(0, 1);
dataEle.addInt(encodedType, 6);
dataEle.addInt(0, 1);
if(_order > 0) {
dataEle.packInt(_samples, unencSampleSize, _start, _skip, _order);
}
//send best data to rice encoder
int paramSize = (lowOrderBits[_order] > 14) ? 5:4;
boolean fiveBitParam = (paramSize < 5) ? false:true;
RiceEncoder.beginResidual(fiveBitParam, (byte)0, dataEle);
/*for(int i = 0; i < errorCount; i++) {
int error = errors[errorOffset+i*errorStep];
if(error >= 32767 || error <= -32767)
System.err.println("Error Bound issue?: " + error);
}*/
rice.encodeRicePartition(_errors, _errorOffset, _errorStep,
_errorCount, dataEle, lowOrderBits[_order], fiveBitParam);
this.lastEncodedSize = dataEle.getTotalBits();
if(DEBUG_LEV > 0)
System.err.println("Subframe_Fixed::encodeSamples(...): End");
return dataEle;
}
public int encodeSamples(int[] samples, int count, int start, int skip,
EncodedElement dataEle, int offset, int unencSampleSize ) {
int encodedSamples = count;
if(DEBUG_LEV > 0) {
System.err.println("Subframe_Fixed::encodeSamples(...) : Begin");
if(DEBUG_LEV > 10) {
System.err.println("--count : " +count);
System.err.println("start:skip:offset:::"+start+":"+skip+":"+offset);
}
}
int increment = skip+1;
//create space for results: Need four sets for the 5 different versions,
// the e0 is sampe as input samples, so no duplicate needed.
if(count != _lastCount) {
_error1 = new int[count];
_error2 = new int[count];
_error3 = new int[count];
_error4 = new int[count];
_lastCount = count;
}
int [] error1 = _error1;
int [] error2 = _error2;
int [] error3 = _error3;
int [] error4 = _error4;
//apply the algorithm to determine errors, summing abs vals as we go
//int sum[] = new int[5];
for(int i = 0; i < 5; i++)
sum[i] = 0;
int tempI;
for(int i = 0; i < count; i++) {
tempI = samples[start+i*increment];
if(tempI < 0) tempI = -tempI;
sum[0] += tempI;
}
for(int i = 1; i < 5; i++) {
error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment];
tempI = error1[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum[1] += tempI;
if(i > 1) {
error2[i] = error1[i]-error1[(i-1)];
tempI = error2[i];
tempI = (tempI < 0) ? -tempI:tempI;
//sum[2] += Math.abs(error2[i]);
sum[2] += tempI;
}
if(i > 2) {
error3[i] = error2[i]-error2[(i-1)];
tempI = error3[i];
tempI = (tempI < 0) ? -tempI:tempI;
//sum[3] += Math.abs(error3[i]);
sum[3] += tempI;
}
if(i > 3) {
error4[i] = error3[i]-error3[(i-1)];
tempI = error4[i];
tempI = (tempI < 0) ? -tempI:tempI;
//sum[4] += Math.abs(error4[i]);
sum[4] += tempI;
}
}
for(int i = 5; i < count; i++) {
error1[i] = samples[start+i*increment]-samples[start+(i-1)*increment];
tempI = error1[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum[1] += tempI;
error2[i] = error1[i]-error1[(i-1)];
tempI = error2[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum[2] += tempI;
error3[i] = error2[i]-error2[(i-1)];
tempI = error3[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum[3] += tempI;
error4[i] = error3[i]-error3[(i-1)];
tempI = error4[i];
tempI = (tempI < 0) ? -tempI:tempI;
sum[4] += tempI;
}
//select best algorithm as indicated by bits needed from sum of values
// and number of priming samples needed.
//int bits[] = new int[5];
//int lowOrderBits[] = new int[5];
int order = 0;
//int lowOrderBits = 0;
for(int i = 0; i < 5; i++) {
double tempLowOrderBits = Math.log(2)*(sum[i]/(count-i));
lowOrderBits[i] = (int)(Math.ceil(Math.log(tempLowOrderBits)/Math.log(2)));
if(lowOrderBits[i] < 0)
lowOrderBits[i] = 0;
else if (lowOrderBits[i] > sampleSize)
lowOrderBits[i] = sampleSize;
lowOrderBits[i]++;//DOUBLE CHECK VALIDITY OF THIS. Decreases the bits needed, but "shouldn't"
//bits[i] = (int)(Math.log(sum[i])/Math.log(10))*(count-1)+sampleSize*i;
bits[i] = (int)(lowOrderBits[i]*(count-i)+sampleSize*i+1);
order = (bits[i] < bits[order]) ? i:order;
}
byte data[] = new byte[bits[order]/8+10];
//byte data[] = new byte[154*count+100];
int[] errors = null;
int errorCount = count-order;
int errorOffset = order;
int errorStep = 1;
switch(order) {
case 0: errors = samples;
errorStep+=skip;
errorOffset=start;break;
case 1: errors = error1;break;
case 2: errors = error2;break;
case 3: errors = error3;break;
case 4: errors = error4;break;
}
//package data(must be done now, due to workings of RiceEncoder)
dataEle.setData(data);
dataEle.setUsableBits(offset);
dataEle.offset = offset;
//write headers
int encodedType = 1<<3 | order;
dataEle.addInt(0, 1);
dataEle.addInt(encodedType, 6);
dataEle.addInt(0, 1);
if(order > 0) {
dataEle.packInt(samples, unencSampleSize, start, skip, order);
}
//send best data to rice encoder
int paramSize = (lowOrderBits[order] > 14) ? 5:4;
boolean fiveBitParam = (paramSize < 5) ? false:true;
RiceEncoder.beginResidual(fiveBitParam, (byte)0, dataEle);
/*for(int i = 0; i < errorCount; i++) {
int error = errors[errorOffset+i*errorStep];
if(error >= 32767 || error <= -32767)
System.err.println("Error Bound issue?: " + error);
}*/
rice.encodeRicePartition(errors, errorOffset, errorStep,
errorCount, dataEle, lowOrderBits[order], fiveBitParam);
this.lastEncodedSize = dataEle.getTotalBits();
if(DEBUG_LEV > 0)
System.err.println("Subframe_Fixed::encodeSamples(...): End");
return encodedSamples;
}
/*private int[] calculatePartitionsCount(int[] errors, int errorOffset, int errorStep,
int errorCount) {
int maxPartitions = 8;
int[][] sums = new int[(int)Math.pow(2,maxPartitions)][];
for(int i = 0; i < maxPartitions; i++) {
sums[i] = new int[(int)Math.pow(2, i)];
}
int[] counts = new int[maxPartitions];
int[] usedPartitions = new int[maxPartitions];
int[] lastPartitionCount = new int[maxPartitions];
for(int i = 0; i < maxPartitions; i++) {
counts[i] = errorCount/sums[i].length;
if(errorCount % sums[i].length != 0) counts[i]++;
usedPartitions[i] = errorCount/counts[i];
if(errorCount %counts[i] != 0) usedPartitions[i]++;
}
for(int i = 0; i < maxPartitions; i++) {
int temp = errorCount+errorOffset/
}
int[] sizes = new int[maxPartitions];
int temp = 0;
double log2 = Math.log(2);
for(int i = 0; i < errorCount; i++) {
temp = errors[errorOffset+i*errorStep];
if(temp < 0) temp = -temp;
for(int x = 0; x < maxPartitions; x++) {
float destDiv = i/counts[x];
int destIndex = (int)(i/destDiv);
sums[x][destIndex] += temp;
}
}
//sum up all bit sizes per partition, choose best size.
for(int i = 0; i < maxPartitions; i++) {
int tempTotal = 0;
sizes[i] = 0;
for(int x = 0; x < usedPartitions[i]; x++) {
tempTotal = (int)(Math.log(sums[i][x])/Math.log(2));
float destDiv = (float)errorCount/sums[x].length;
int destCount = errorCount/sums[x].length;
int destIndex = (int)(i/destDiv);
sizes[i] += tempTotal+4+;
}
}
return results;
}*/
}