/* * Copyright (C) 2011 in-somnia * * This file is part of JAAD. * * JAAD 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 3 of the * License, or (at your option) any later version. * * JAAD 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, see <http://www.gnu.org/licenses/>. */ 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; //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; int groupOff = 0, idx = 0; for(int g = 0; g<windowGroups; g++) { int groupLen = info.getWindowGroupLength(g); for(sfb = 0; sfb<maxSFB; sfb++, idx++) { hcb = sfbCB[idx]; off = groupOff+offsets[sfb]; width = offsets[sfb+1]-offsets[sfb]; if(hcb==ZERO_HCB||hcb==INTENSITY_HCB||hcb==INTENSITY_HCB2) { for(w = 0; w<groupLen; w++, off += 128) { Arrays.fill(data, off, off+width, 0); } } else if(hcb==NOISE_HCB) { //apply PNS: fill with random values for(w = 0; w<groupLen; w++, off += 128) { float energy = 0; for(k = 0; k<width; k++) { randomState *= 1664525+1013904223; data[off+k] = randomState; energy += data[off+k]*data[off+k]; } final float scale = (float) (scaleFactors[idx]/Math.sqrt(energy)); for(k = 0; k<width; k++) { data[off+k] *= scale; } } } else { for(w = 0; w<groupLen; w++, off += 128) { num = (hcb>=FIRST_PAIR_HCB) ? 2 : 4; for(k = 0; k<width; k += num) { Huffman.decodeSpectralData(in, hcb, buf, 0); //inverse quantization & scaling for(j = 0; j<num; j++) { data[off+k+j] = (buf[j]>0) ? IQ_TABLE[buf[j]] : -IQ_TABLE[-buf[j]]; data[off+k+j] *= scaleFactors[idx]; } } } } } groupOff += groupLen<<7; } } /* =========== gets ============ */ /** * Does inverse quantization and applies the scale factors on the decoded * data. After this the noiseless decoding is finished and the decoded data * is returned. * @return the inverse quantized and scaled data */ public float[] getInvQuantData() throws AACException { return data; } public ICSInfo getInfo() { return info; } public int[] getSectEnd() { return sectEnd; } public int[] getSfbCB() { return sfbCB; } public float[] getScaleFactors() { return scaleFactors; } public boolean isTNSDataPresent() { return tnsDataPresent; } public TNS getTNS() { return tns; } public int getGlobalGain() { return globalGain; } public boolean isNoiseUsed() { return noiseUsed; } public int getLongestCodewordLength() { return longestCodewordLen; } public int getReorderedSpectralDataLength() { return reorderedSpectralDataLen; } public boolean isGainControlPresent() { return gainControlPresent; } public GainControl getGainControl() { return gainControl; } }