net.sourceforge.javaflacencoder.RiceEncoder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javasound-flac Show documentation
Show all versions of javasound-flac Show documentation
A port of the Free Lossless Audio Codec (FLAC) decoder to Java and a FLAC encoder implemented in Java.
/*
* 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 net.sourceforge.javaflacencoder;
/**
* The RiceEncoder class is used to create FLAC-compliant rice-encoded
* residuals.
*
* @author Preston Lacey
*/
public class RiceEncoder {
/** For debugging: Higher values equals greater output, generally in
* increments of 10 */
public static int DEBUG_LEV = 0;
private static final int POSITIVE = 0;
private static final int NEGATIVE = 1;
private static final int STOP_BIT = 0xFFFFFFFF;
private static final int UNARY_BIT = 0;
private int[] _dataToEncode = null;
private int[] _bitsToEncode = null;
/**
* Constructor. A RiceEncoder object is used(as opposed to potentially
* faster static methods), for memory considerations; some temporary,
* dynamically created arrays are kept between calls to the encode methods
* to prevent frequently allocating and freeing similarly sized arrays.
*/
public RiceEncoder() {
}
/**
* Create the residual headers for a FLAC stream.
*
* @param useFiveBitParam Set TRUE if using a five-bit parameter size, FALSE
* for a four-bit parameter
* @param order Specify order of partitions to be used(actual number of
* partitions will be 2^order.
* @param ele EncodedElement to write header to.
* @return total written size of header.
*/
public static int beginResidual(boolean useFiveBitParam, byte order,
EncodedElement ele) {
ele = ele.getEnd();
int paramSize = (useFiveBitParam) ? 1:0;
ele.addInt(paramSize, 2);
ele.addInt(order, 4);
return 6;
}
public static int encodeRicePartitionEscaped(int[] values, int inputOffset,
int inputStep, int inputCount, EncodedElement destEle,
int bitParam, boolean fiveBitParam) {
if(DEBUG_LEV > 0)
System.err.println("RiceEncoder::encode : Begin");
/* Currently, we're passing in an EncodedElement with a set byte[], and
filling that array. We should therefore ensure that we're not writing
too much to it. We *can* add another element to the given one if need.*/
//write headers(i.e, write the parameter)
int bitsWritten = 0;
if(fiveBitParam) {
destEle.addInt(255, 5);
destEle.addInt(16,5);
bitsWritten += 10;
}
else {
destEle.addInt(255, 4);
destEle.addInt(16,5);
bitsWritten += 9;
}
for(int i = 0; i < inputCount; i++) {
destEle.addInt(values[i*inputStep+inputOffset], 16);
bitsWritten += 16;
}
if(DEBUG_LEV > 0)
System.err.println("RiceEncoder::encode : End");
return bitsWritten;
}
/**
* Rice-encode a set of values, adding necessary headers for FLAC format. This
* encodes a single rice-partition. In general, beginResidual(...) should be
* used before this method.
*
* @param values array of integer values to save
* @param inputOffset start index in input array
* @param inputStep number of values to skip between target values(for
* interleaved data.
* @param inputCount number of total values to encode
* @param bitParam rice-parameter to use. This value should be based upon
* the distribution of the input data(roughly speeking, each value
* will require at least bitParam+1 bits to save, so this value
* should reflect the average magnitude of input values.
* @param destEle EncodedElement to save result to.
* @param fiveBitParam Set true if this header should use a five-bit
* rice-parameter, false for a four bit parameter.
* @return total encoded size(including headers)
*/
public int encodeRicePartition(int[] values, int inputOffset,
int inputStep, int inputCount, EncodedElement destEle,
final int bitParam, boolean fiveBitParam) {
//Pack int version of encode partition
if(DEBUG_LEV > 0) {
System.err.println("RiceEncoder::encode : Begin");
System.err.println("-- bitParam: " + bitParam);
}
//write headers(i.e, write the parameter)
int startBits = destEle.getTotalBits();
if(fiveBitParam) {
destEle.addInt(bitParam, 5);
}
else {
destEle.addInt(bitParam, 4);
}
//encode each input value;
if(_dataToEncode == null || _bitsToEncode == null) {
_dataToEncode = new int[values.length*4];
_bitsToEncode = new int[values.length*4];
}
int[] dataToEncode = _dataToEncode;
int[] bitsToEncode = _bitsToEncode;
int nextToEncode = 0;
int maxToEncode = dataToEncode.length;
int inputIndex = inputOffset-inputStep;
final int stopbit = (bitParam >0) ? STOP_BIT << bitParam:STOP_BIT;
final int maskParam = (bitParam == 0) ? 0:0xFFFFFFFF>>>(32-bitParam);
for(int i = 0; i < inputCount; i++) {
inputIndex +=inputStep;
int value = values[inputIndex];
value = (value < 0) ? -2*value-1:2*value;
int upperBits = value >> bitParam;
//make sure we won't write to much. Handle if we will.
int dataToEncodeSpaceNeeded = (2+upperBits/32);
if(upperBits%32 != 0)
dataToEncodeSpaceNeeded++;
if(dataToEncodeSpaceNeeded+nextToEncode >= maxToEncode) {
//write everything we have:
destEle.packIntByBits(dataToEncode, bitsToEncode, 0, nextToEncode);
nextToEncode = 0;
}
//write unary upper bits:
int count = 0;
while(upperBits > 0) {
int tempVal = (upperBits > 32) ? 32:upperBits;//can only write 32 bits at a time.
dataToEncode[nextToEncode] = UNARY_BIT;
bitsToEncode[nextToEncode++] = tempVal;
upperBits -= tempVal;
count++;
}
dataToEncode[nextToEncode] = (value&maskParam) | stopbit ;
bitsToEncode[nextToEncode++] = bitParam+1;
}
//System.err.println("end loop");
//write remaining data to encode
if(nextToEncode > 0) {
destEle.packIntByBits(dataToEncode, bitsToEncode, 0, nextToEncode);
nextToEncode = 0;
}
int bitsWritten = destEle.getTotalBits() - startBits;
//System.err.println("RiceENcoder encode end:");
return bitsWritten;
}
/**
* Calculate how large a given set of values will be once it has been
* rice-encoded. While this method duplicates much of the process of
* rice-encoding, it is faster than an actual encode since the data is not
* actually written to the flac bitstream format(a rather costly write).
*
* @param values array of integer values to save
* @param inputOffset start index in input array
* @param inputStep number of values to skip between target values(for
* interleaved data.
* @param inputCount number of total values to encode
* @param bitParam rice-parameter to use. This value should be based upon
* the distribution of the input data(roughly speeking, each value
* will require at least bitParam+1 bits to save, so this value
* should reflect the average magnitude of input values.
* @return total encoded-size with given data and rice-parameter.
*/
public static int calculateEncodeSize(int[] values, int inputOffset,
int inputStep, int inputCount, int bitParam) {
//Pack int version of encode partition
if(DEBUG_LEV > 0) {
System.err.println("RiceEncoder::calculateEncodeSize : Begin");
System.err.println("-- bitParam: " + bitParam);
}
int totalEncodeLength = inputCount*(bitParam+1);
int index = inputOffset-inputStep;
for(int i = 0; i < inputCount; i++) {
index += inputStep;
int value = values[index];
value = (value < 0) ? -2*value-1:2*value;
int upperBits = value >> bitParam;
totalEncodeLength += upperBits;
}
if(bitParam > 14)
totalEncodeLength += 5+6;
else
totalEncodeLength += 4+6;
return totalEncodeLength;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy