gt: 		int groupOff = 0, idx = 0;
context: package net.sourceforge.jaad.aac.syntax;
  
  import java.util.Arrays;
  import net.sourceforge.jaad.aac.AACException;
  import net.sourceforge.jaad.aac.ChannelConfiguration;
  import net.sourceforge.jaad.aac.DecoderConfig;
  import net.sourceforge.jaad.aac.error.RVLC;
  import net.sourceforge.jaad.aac.gain.GainControl;
  import net.sourceforge.jaad.aac.huffman.HCB;
  import net.sourceforge.jaad.aac.huffman.Huffman;
  import net.sourceforge.jaad.aac.tools.TNS;
  import java.util.logging.Level;
  import net.sourceforge.jaad.aac.Decoder;
  
  //TODO: apply pulse data
  public class ICStream implements Constants, HCB, ScaleFactorTable, IQTable {
  
  	private static final int SF_DELTA = 60;
  	private static final int SF_OFFSET = 200;
  	private static int randomState = 0x1F2E3D4C;
  	private final int frameLength;
  	//always needed
  	private final ICSInfo info;
  	private final int[] sfbCB;
  	private final int[] sectEnd;
  	private final float[] data;
  	private final float[] scaleFactors;
  	private int globalGain;
  	private boolean pulseDataPresent, tnsDataPresent, gainControlPresent;
  	//only allocated if needed
  	private TNS tns;
  	private GainControl gainControl;
  	private int[] pulseOffset, pulseAmp;
  	private int pulseCount;
  	private int pulseStartSWB;
  	//error resilience
  	private boolean noiseUsed;
  	private int reorderedSpectralDataLen, longestCodewordLen;
  	private RVLC rvlc;
  
  	public ICStream(int frameLength) {
  		this.frameLength = frameLength;
  		info = new ICSInfo(frameLength);
  		sfbCB = new int[MAX_SECTIONS];
  		sectEnd = new int[MAX_SECTIONS];
  		data = new float[frameLength];
  		scaleFactors = new float[MAX_SECTIONS];
  	}
  
  	/* ========= decoding ========== */
  	public void decode(BitStream in, boolean commonWindow, DecoderConfig conf) throws AACException {
  		if(conf.isScalefactorResilienceUsed()&&rvlc==null) rvlc = new RVLC();
  		final boolean er = conf.getProfile().isErrorResilientProfile();
  
  		globalGain = in.readBits(8);
  
  		if(!commonWindow) info.decode(in, conf, commonWindow);
  
  		decodeSectionData(in, conf.isSectionDataResilienceUsed());
  
  		//if(conf.isScalefactorResilienceUsed()) rvlc.decode(in, this, scaleFactors);
  		/*else*/ decodeScaleFactors(in);
  
  		pulseDataPresent = in.readBool();
  		if(pulseDataPresent) {
  			if(info.isEightShortFrame()) throw new AACException("pulse data not allowed for short frames");
  			LOGGER.log(Level.FINE, "PULSE");
  			decodePulseData(in);
  		}
  
  		tnsDataPresent = in.readBool();
  		if(tnsDataPresent&&!er) {
  			if(tns==null) tns = new TNS();
  			tns.decode(in, info);
  		}
  
  		gainControlPresent = in.readBool();
  		if(gainControlPresent) {
  			if(gainControl==null) gainControl = new GainControl(frameLength);
  			LOGGER.log(Level.FINE, "GAIN");
  			gainControl.decode(in, info.getWindowSequence());
  		}
  
  		//RVLC spectral data
  		//if(conf.isScalefactorResilienceUsed()) rvlc.decodeScalefactors(this, in, scaleFactors);
  
  		if(conf.isSpectralDataResilienceUsed()) {
  			int max = (conf.getChannelConfiguration()==ChannelConfiguration.CHANNEL_CONFIG_STEREO) ? 6144 : 12288;
  			reorderedSpectralDataLen = Math.max(in.readBits(14), max);
  			longestCodewordLen = Math.max(in.readBits(6), 49);
  			//HCR.decodeReorderedSpectralData(this, in, data, conf.isSectionDataResilienceUsed());
  		}
  		else decodeSpectralData(in);
  	}
  
  	public void decodeSectionData(BitStream in, boolean sectionDataResilienceUsed) throws AACException {
  		Arrays.fill(sfbCB, 0);
  		Arrays.fill(sectEnd, 0);
  		final int bits = info.isEightShortFrame() ? 3 : 5;
  		final int escVal = (1<<bits)-1;
  
  		final int windowGroupCount = info.getWindowGroupCount();
  		final int maxSFB = info.getMaxSFB();
  
  		int end, cb, incr;
  		int idx = 0;
  
  		for(int g = 0; g<windowGroupCount; g++) {
  			int k = 0;
  			while(k<maxSFB) {
  				end = k;
  				cb = in.readBits(4);
  				if(cb==12) throw new AACException("invalid huffman codebook: 12");
  				while((incr = in.readBits(bits))==escVal) {
  					end += incr;
  				}
  				end += incr;
  				if(end>maxSFB) throw new AACException("too many bands: "+end+", allowed: "+maxSFB);
  				for(; k<end; k++) {
  					sfbCB[idx] = cb;
  					sectEnd[idx++] = end;
  				}
  			}
  		}
  	}
  
  	private void decodePulseData(BitStream in) throws AACException {
  		pulseCount = in.readBits(2)+1;
  		pulseStartSWB = in.readBits(6);
  		if(pulseStartSWB>=info.getSWBCount()) throw new AACException("pulse SWB out of range: "+pulseStartSWB+" > "+info.getSWBCount());
  
  		if(pulseOffset==null||pulseCount!=pulseOffset.length) {
  			//only reallocate if needed
  			pulseOffset = new int[pulseCount];
  			pulseAmp = new int[pulseCount];
  		}
  
  		pulseOffset[0] = info.getSWBOffsets()[pulseStartSWB];
  		pulseOffset[0] += in.readBits(5);
  		pulseAmp[0] = in.readBits(4);
  		for(int i = 1; i<pulseCount; i++) {
  			pulseOffset[i] = in.readBits(5)+pulseOffset[i-1];
  			if(pulseOffset[i]>1023) throw new AACException("pulse offset out of range: "+pulseOffset[0]);
  			pulseAmp[i] = in.readBits(4);
  		}
  	}
  
  	public void decodeScaleFactors(BitStream in) throws AACException {
  		final int windowGroups = info.getWindowGroupCount();
  		final int maxSFB = info.getMaxSFB();
  		//0: spectrum, 1: noise, 2: intensity
  		final int[] offset = {globalGain, globalGain-90, 0};
  
  		int tmp;
  		boolean noiseFlag = true;
  
  		int sfb, idx = 0;
  		for(int g = 0; g<windowGroups; g++) {
  			for(sfb = 0; sfb<maxSFB;) {
  				int end = sectEnd[idx];
  				switch(sfbCB[idx]) {
  					case ZERO_HCB:
  						for(; sfb<end; sfb++, idx++) {
  							scaleFactors[idx] = 0;
  						}
  						break;
  					case INTENSITY_HCB:
  					case INTENSITY_HCB2:
  						for(; sfb<end; sfb++, idx++) {
  							offset[2] += Huffman.decodeScaleFactor(in)-SF_DELTA;
  							tmp = Math.min(Math.max(offset[2], -155), 100);
  							scaleFactors[idx] = SCALEFACTOR_TABLE[-tmp+SF_OFFSET];
  						}
  						break;
  					case NOISE_HCB:
  						for(; sfb<end; sfb++, idx++) {
  							if(noiseFlag) {
  								offset[1] += in.readBits(9)-256;
  								noiseFlag = false;
  							}
  							else offset[1] += Huffman.decodeScaleFactor(in)-SF_DELTA;
  							tmp = Math.min(Math.max(offset[1], -100), 155);
  							scaleFactors[idx] = -SCALEFACTOR_TABLE[tmp+SF_OFFSET];
  						}
  						break;
  					default:
  						for(; sfb<end; sfb++, idx++) {
  							offset[0] += Huffman.decodeScaleFactor(in)-SF_DELTA;
  							if(offset[0]>255) throw new AACException("scalefactor out of range: "+offset[0]);
  							scaleFactors[idx] = SCALEFACTOR_TABLE[offset[0]-100+SF_OFFSET];
  						}
  						break;
  				}
  			}
  		}
  	}
  
  	private void decodeSpectralData(BitStream in) throws AACException {
  		Arrays.fill(data, 0);
  		final int maxSFB = info.getMaxSFB();
  		final int windowGroups = info.getWindowGroupCount();
  		final int[] offsets = info.getSWBOffsets();
  		final int[] buf = new int[4];
  
  		int sfb, j, k, w, hcb, off, width, num;
  