All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sourceforge.javaflacencoder.Subframe_Fixed Maven / Gradle / Ivy

Go to download

javaFlacEncoder is a pure java implementation of a FLAC encoder library. It is designed to allow third-party java applications to enable flac encoding without resorting to use of JNI or scripted file conversions. Additionally, a basic console wav->flac encoding tool is included as part of this package. Original project can be found here: http://javaflacencoder.sourceforge.net/

The newest version!
/*
 * 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;

/**
 * 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;
  }

  public EncodedElement getData() {
    EncodedElement dataEle = new EncodedElement(_totalBits/8+1,_offset);
    getData(dataEle);
    return dataEle;
  }
  /**
   * 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) {
    //EncodedElement dataEle = new EncodedElement(_totalBits/8+1,_offset);
    int startSize = dataEle.getTotalBits();
    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()-startSize;
    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 = 0;
    encodedSamples = encodeSamples(samples, count, start, skip, offset, unencSampleSize);
    dataEle.clear(_totalBits/8+1,offset);
    getData(dataEle);
    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;
    }*/

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy