package org.jcodec.containers.mkv;
import static org.jcodec.containers.mkv.MKVType.Attachments;
import static org.jcodec.containers.mkv.MKVType.Chapters;
import static org.jcodec.containers.mkv.MKVType.Cluster;
import static org.jcodec.containers.mkv.MKVType.Cues;
import static org.jcodec.containers.mkv.MKVType.Info;
import static org.jcodec.containers.mkv.MKVType.SeekHead;
import static org.jcodec.containers.mkv.MKVType.Tags;
import static org.jcodec.containers.mkv.MKVType.Tracks;
import static org.jcodec.containers.mkv.MKVType.createById;
import static org.jcodec.containers.mkv.util.EbmlUtil.toHexString;

import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.containers.mkv.boxes.EbmlBase;
import org.jcodec.containers.mkv.boxes.EbmlBin;
import org.jcodec.containers.mkv.boxes.EbmlMaster;
import org.jcodec.containers.mkv.boxes.EbmlVoid;
import org.jcodec.containers.mkv.util.EbmlUtil;

import java.io.IOException;
import java.lang.System;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed under FreeBSD License
 *
 * EBML IO implementation
 *
 * @author The JCodec project
 *
 */
public class MKVParser {

    private SeekableByteChannel channel;
    private LinkedList<EbmlMaster> trace;

    public MKVParser(SeekableByteChannel channel) {
        this.channel = channel;
        this.trace = new LinkedList<EbmlMaster>();

    }

    public List<EbmlMaster> parse() throws IOException {
        List<EbmlMaster> tree = new ArrayList<EbmlMaster>();
        EbmlBase e = null;

        while ((e = nextElement()) != null) {
            if (!isKnownType(e.id))
                System.err.println("Unspecified header: " + EbmlUtil.toHexString(e.id) + " at " + e.offset);

            while (!possibleChild(trace.peekFirst(), e))
                closeElem(trace.removeFirst(), tree);

            openElem(e);

            if (e instanceof EbmlMaster) {
                trace.push((EbmlMaster) e);
            } else if (e instanceof EbmlBin) {
                EbmlBin bin = (EbmlBin) e;
                EbmlMaster traceTop = trace.peekFirst();
                if ((traceTop.dataOffset + traceTop.dataLen) < (e.dataOffset + e.dataLen)) {
                    channel.setPosition((traceTop.dataOffset + traceTop.dataLen));
                } else
                    try {
                        bin.readChannel(channel);
                    } catch (OutOfMemoryError oome) {
                        throw new RuntimeException(e.type + " 0x" + toHexString(bin.id) + " size: " + Long.toHexString(bin.dataLen) + " offset: 0x" + Long.toHexString(e.offset), oome);
                    }
                trace.peekFirst().add(e);
            } else if (e instanceof EbmlVoid) {
                ((EbmlVoid) e).skip(channel);
            } else {
                throw new RuntimeException("Currently there are no elements that are neither Master nor Binary, should never actually get here");
            }

        }

        while (trace.peekFirst() != null)
            closeElem(trace.removeFirst(), tree);

        return tree;
    }

    private boolean possibleChild(EbmlMaster parent, EbmlBase child) {
        if (parent != null && Cluster.equals(parent.type) && child != null && !Cluster.equals(child.type) && !Info.equals(child.type) && !SeekHead.equals(child.type) && !Tracks.equals(child.type)
                && !Cues.equals(child.type) && !Attachments.equals(child.type) && !Tags.equals(child.type) && !Chapters.equals(child.type))
            return true;

        return MKVType.possibleChild(parent, child);
    }

    private void openElem(EbmlBase e) {
        /*
         * Whatever logging you would like to have. Here's just one example
         */
        // System.out.println(e.type.name() + (e instanceof EbmlMaster ? " master " : "") + " id: " + printAsHex(e.id) + " off: 0x" + toHexString(e.offset).toUpperCase() + " data off: 0x" +
        // toHexString(e.dataOffset).toUpperCase() + " len: 0x" + toHexString(e.dataLen).toUpperCase());
    }

    private void closeElem(EbmlMaster e, List<EbmlMaster> tree) {
        if (trace.peekFirst() == null) {
            tree.add(e);
        } else {
            trace.peekFirst().add(e);
        }
    }

    private EbmlBase nextElement() throws IOException {
        long offset = channel.position();
        if (offset >= channel.size())
            return null;

        byte[] typeId = MKVParser.readEbmlId(channel);

        while ((typeId == null && !isKnownType(typeId)) && offset < channel.size()) {
            offset++;
            channel.setPosition(offset);
            typeId = MKVParser.readEbmlId(channel);
        }

        long dataLen = MKVParser.readEbmlInt(channel);

        EbmlBase elem = createById(typeId, offset);
        elem.offset = offset;
        elem.typeSizeLength = (int) (channel.position() - offset);
        elem.dataOffset = channel.position();

        elem.dataLen = (int) dataLen;

        return elem;
    }

    public boolean isKnownType(byte[] b) {
        if (!trace.isEmpty() && Cluster.equals(trace.peekFirst().type))
            return true;

        return MKVType.isSpecifiedHeader(b);
    }

    /**
     * Reads an EBML id from the channel. EBML ids have length encoded inside of them For instance, all one-byte ids have first byte set to '1', like 0xA3 or 0xE7, whereas the two-byte ids have first
     * byte set to '0' and second byte set to '1', thus: 0x42 0x86  or 0x42 0xF7
     *
     * @return byte array filled with the ebml id
     * @throws IOException
     */
    static public byte[] readEbmlId(SeekableByteChannel source) throws IOException {
        if (source.position() == source.size())
            return null;

        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.limit(1);
        source.read(buffer);
        buffer.flip();

        byte firstByte = buffer.get();
        int numBytes = EbmlUtil.computeLength(firstByte);

        if (numBytes == 0)
            return null;

        if (numBytes > 1) {
            buffer.limit(numBytes);
            source.read(buffer);
        }

        buffer.flip();
        ByteBuffer val = ByteBuffer.allocate(buffer.remaining());
        val.put(buffer);
        return val.array();
    }

    static public long readEbmlInt(SeekableByteChannel source) throws IOException {

        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.limit(1);

        source.read(buffer);
        buffer.flip();

        // read the first byte
        byte firstByte = (byte) buffer.get();
        int length = EbmlUtil.computeLength(firstByte);

        if (length == 0)
            throw new RuntimeException("Invalid ebml integer size.");

        // read the reset
        buffer.limit(length);
        source.read(buffer);
        buffer.position(1);

        // use the first byte
        long value = firstByte & (0xFF >>> length);
        length--;

        // use the reset
        while(length > 0){
            value = (value << 8) | (buffer.get() & 0xff);
            length--;
        }

        return value;
    }

}


package org.jcodec.containers.flv;
import org.jcodec.common.AudioFormat;
import org.jcodec.common.Codec;
import org.jcodec.common.io.NIOUtils;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.logging.Logger;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.containers.flv.FLVTag.AacAudioTagHeader;
import org.jcodec.containers.flv.FLVTag.AudioTagHeader;
import org.jcodec.containers.flv.FLVTag.AvcVideoTagHeader;
import org.jcodec.containers.flv.FLVTag.TagHeader;
import org.jcodec.containers.flv.FLVTag.Type;
import org.jcodec.containers.flv.FLVTag.VideoTagHeader;
import org.jcodec.platform.Platform;

import java.io.IOException;
import java.lang.System;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ReadableByteChannel;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * FLV ( Flash Media Video ) demuxer
 *
 * @author Stan Vitvitskyy
 *
 */
public class FLVReader {

    private static final int REPOSITION_BUFFER_READS = 10;
    private static final int TAG_HEADER_SIZE = 15;
    // Read buffer, 1M
    private static final int READ_BUFFER_SIZE = 1 << 10;
    private int frameNo;
    private ByteBuffer readBuf;
    private SeekableByteChannel ch;
    private boolean eof;

    private static boolean platformBigEndian = ByteBuffer.allocate(0).order() == ByteOrder.BIG_ENDIAN;

    public static Codec[] audioCodecMapping = new Codec[] { Codec.PCM, Codec.ADPCM, Codec.MP3, Codec.PCM,
            Codec.NELLYMOSER, Codec.NELLYMOSER, Codec.NELLYMOSER, Codec.G711, Codec.G711, null, Codec.AAC, Codec.SPEEX,
            Codec.MP3, null };
    public static Codec[] videoCodecMapping = new Codec[] { null, null, Codec.SORENSON, Codec.FLASH_SCREEN_VIDEO,
            Codec.VP6, Codec.VP6, Codec.FLASH_SCREEN_V2, Codec.H264 };

    public static int[] sampleRates = new int[] { 5500, 11000, 22000, 44100 };

    public FLVReader(SeekableByteChannel ch) throws IOException {
        this.ch = ch;
        readBuf = ByteBuffer.allocate(READ_BUFFER_SIZE);
        readBuf.order(ByteOrder.BIG_ENDIAN);

        initialRead(ch);

        if (!readHeader(readBuf)) {
            // This file doesn't have an FLV header, maybe it's a portion of an
            // FLV file and we can position at the tag start?
            readBuf.position(0);
            if (!repositionFile())
                throw new RuntimeException("Invalid FLV file");
            else {
                Logger.warn(String.format("Parsing a corrupt FLV file, first tag found at %d. %s", readBuf.position(),
                        readBuf.position() == 0 ? "Did you forget the FLV 9-byte header?" : ""));
            }
        }
    }

    private void initialRead(ReadableByteChannel ch) throws IOException {
        readBuf.clear();
        if (ch.read(readBuf) == -1)
            eof = true;
        readBuf.flip();
    }

    public FLVTag readNextPacket() throws IOException {
        if (eof)
            return null;

        FLVTag pkt = parsePacket(readBuf);
        // No more pakets fit into the buffer, reading more data
        if (pkt == null && !eof) {
            moveRemainderToTheStart(readBuf);
            if (ch.read(readBuf) == -1) {
                eof = true;
                return null;
            }

            while (MathUtil.log2(readBuf.capacity()) <= 22) {
                readBuf.flip();
                pkt = parsePacket(readBuf);
                if (pkt != null || readBuf.position() > 0)
                    break;
                // The buffer is too small, getting a bigger one
                ByteBuffer newBuf = ByteBuffer.allocate(readBuf.capacity() << 2);
                newBuf.put(readBuf);
                readBuf = newBuf;
                if (ch.read(readBuf) == -1) {
                    eof = true;
                    return null;
                }
            }
        }

        return pkt;
    }

    public FLVTag readPrevPacket() throws IOException {
        int startOfLastPacket = readBuf.getInt();
        readBuf.position(readBuf.position() - 4);
        if (readBuf.position() > startOfLastPacket) {
            // The previous frame is still in the buffer, so no need to fetch
            readBuf.position(readBuf.position() - startOfLastPacket);
            return parsePacket(readBuf);
        } else {
            // Now we need to fetch the new buffer, because we are unsure of the
            // access pattern we are going to fetch only half of the buffer from
            // the left side and the other half from the right side of the
            // current position
            long oldPos = ch.position() - readBuf.remaining();
            if (oldPos <= 9) {
                // We are at the first frame, there's nowhere to seek
                return null;
            }
            long newPos = Math.max(0, oldPos - readBuf.capacity() / 2);

            ch.setPosition(newPos);
            readBuf.clear();
            ch.read(readBuf);
            readBuf.flip();
            readBuf.position((int) (oldPos - newPos));
            return readPrevPacket();
        }
    }

    private static void moveRemainderToTheStart(ByteBuffer readBuf) {
        int rem = readBuf.remaining();
        for (int i = 0; i < rem; i++) {
            readBuf.put(i, readBuf.get());
        }
        readBuf.clear();
        readBuf.position(rem);
    }

    public FLVTag parsePacket(ByteBuffer readBuf) throws IOException {
        for (;;) {
            if (readBuf.remaining() < TAG_HEADER_SIZE) {
                return null;
            }

            int pos = readBuf.position();
            long packetPos = ch.position() - readBuf.remaining();
            int prevPacketSize = readBuf.getInt();
            int packetType = readBuf.get() & 0xff;
            int payloadSize = ((readBuf.getShort() & 0xffff) << 8) | (readBuf.get() & 0xff);
            int timestamp = ((readBuf.getShort() & 0xffff) << 8) | (readBuf.get() & 0xff)
                    | ((readBuf.get() & 0xff) << 24);
            int streamId = ((readBuf.getShort() & 0xffff) << 8) | (readBuf.get() & 0xff);

            // Sanity check and reposition
            if (readBuf.remaining() >= payloadSize + 4) {
                int thisPacketSize = readBuf.getInt(readBuf.position() + payloadSize);
                if (thisPacketSize != payloadSize + 11) {
                    readBuf.position(readBuf.position() - TAG_HEADER_SIZE);
                    if (!repositionFile()) {
                        Logger.error(String.format("Corrupt FLV stream at %d, failed to reposition!", packetPos));
                        ch.setPosition(ch.size());
                        eof = true;
                        return null;
                    }
                    Logger.warn(String.format("Corrupt FLV stream at %d, repositioned to %d.", packetPos, ch.position()
                            - readBuf.remaining()));
                    continue;
                }
            }

            if (readBuf.remaining() < payloadSize) {
                readBuf.position(pos);
                return null;
            }

            if (packetType != 0x8 && packetType != 0x9 && packetType != 0x12) {
                NIOUtils.skip(readBuf, payloadSize);
                continue;
            }

            ByteBuffer payload = NIOUtils.clone(NIOUtils.read(readBuf, payloadSize));

            Type type;
            TagHeader tagHeader;
            if (packetType == 0x8) {
                type = Type.AUDIO;
                tagHeader = parseAudioTagHeader(payload.duplicate());
            } else if (packetType == 0x9) {
                type = Type.VIDEO;
                tagHeader = parseVideoTagHeader(payload.duplicate());
            } else if (packetType == 0x12) {
                type = Type.SCRIPT;
                tagHeader = null;
            } else {
                System.out.println("NON AV packet");
                continue;
            }
            boolean keyFrame = false;
            if (tagHeader != null && tagHeader instanceof VideoTagHeader) {
                VideoTagHeader vth = (VideoTagHeader) tagHeader;
                keyFrame &= vth.getFrameType() == 1;
            }
            keyFrame &= packetType == 0x8 || packetType == 9;

            return new FLVTag(type, packetPos, tagHeader, timestamp, payload, keyFrame, frameNo++, streamId,
                    prevPacketSize);
        }
    }

    public static boolean readHeader(ByteBuffer readBuf) {
        if (readBuf.remaining() < 9 || readBuf.get() != 'F' || readBuf.get() != 'L' || readBuf.get() != 'V'
                || readBuf.get() != 1 || (readBuf.get() & 0x5) == 0 || readBuf.getInt() != 9) {
            return false;
        }
        return true;
    }

    public static FLVMetadata parseMetadata(ByteBuffer bb) {
        if ("onMetaData".equals(readAMFData(bb, -1)))
            return new FLVMetadata((Map<String, Object>) readAMFData(bb, -1));
        return null;
    }

    private static Object readAMFData(ByteBuffer input, int type) {
        if (type == -1) {
            type = input.get() & 0xff;
        }
        switch (type) {
        case 0:
            return input.getDouble();
        case 1:
            return input.get() == 1;
        case 2:
            return readAMFString(input);
        case 3:
            return readAMFObject(input);
        case 8:
            return readAMFEcmaArray(input);
        case 10:
            return readAMFStrictArray(input);
        case 11:
            final Date date = new Date((long) input.getDouble());
            input.getShort(); // time zone
            return date;
        case 13:
            return "UNDEFINED";
        default:
            return null;
        }
    }

    private static Object readAMFStrictArray(ByteBuffer input) {
        int count = input.getInt();
        Object[] result = new Object[count];
        for (int i = 0; i < count; i++) {
            result[i] = readAMFData(input, -1);
        }
        return result;
    }

    private static String readAMFString(ByteBuffer input) {
        int size = input.getShort() & 0xffff;
        return Platform.stringFromCharset(NIOUtils.toArray(NIOUtils.read(input, size)), Platform.UTF_8);
    }

    private static Object readAMFObject(ByteBuffer input) {
        Map<String, Object> array = new HashMap<String, Object>();
        while (true) {
            String key = readAMFString(input);
            int dataType = input.get() & 0xff;
            if (dataType == 9) { // object end marker
                break;
            }
            array.put(key, readAMFData(input, dataType));
        }
        return array;
    }

    private static Object readAMFEcmaArray(ByteBuffer input) {
        long size = input.getInt();
        Map<String, Object> array = new HashMap<String, Object>();
        for (int i = 0; i < size; i++) {
            String key = readAMFString(input);
            int dataType = input.get() & 0xff;
            array.put(key, readAMFData(input, dataType));
        }
        return array;
    }

    public static VideoTagHeader parseVideoTagHeader(ByteBuffer dup) {
        byte b0 = dup.get();
        int frameType = (b0 & 0xff) >> 4;
        int codecId = (b0 & 0xf);
        Codec codec = videoCodecMapping[codecId];

        if (codecId == 7) {
            byte avcPacketType = dup.get();
            int compOffset = (dup.getShort() << 8) | (dup.get() & 0xff);
            return new AvcVideoTagHeader(codec, frameType, avcPacketType, compOffset);
        }

        return new VideoTagHeader(codec, frameType);
    }

    public static TagHeader parseAudioTagHeader(ByteBuffer dup) {
        byte b = dup.get();

        int codecId = (b & 0xff) >> 4;
        int sampleRate = sampleRates[(b >> 2) & 0x3];
        if (codecId == 4 || codecId == 11)
            sampleRate = 16000;
        if (codecId == 5 || codecId == 14)
            sampleRate = 8000;

        int sampleSizeInBits = (b & 0x2) == 0 ? 8 : 16;
        boolean signed = codecId != 3 && codecId != 0 || sampleSizeInBits == 16;
        int channelCount = 1 + (b & 1);
        if (codecId == 11)
            channelCount = 1;

        AudioFormat audioFormat = new AudioFormat(sampleRate, sampleSizeInBits, channelCount, signed,
                codecId == 3 ? false : platformBigEndian);
        Codec codec = audioCodecMapping[codecId];
        if (codecId == 10) {
            byte packetType = dup.get();
            return new AacAudioTagHeader(codec, audioFormat, packetType);
        }

        return new AudioTagHeader(codec, audioFormat);
    }

    public static int probe(ByteBuffer buf) {
        try {
            readHeader(buf);
            return 100;
        } catch (RuntimeException e) {
            return 0;
        }
    }

    public void reset() throws IOException {
        initialRead(ch);
    }

    public void reposition() throws IOException {
        reset();

        if (!positionAtPacket(readBuf)) {
            throw new RuntimeException("Could not find at FLV tag start");
        }
    }

    public static boolean positionAtPacket(ByteBuffer readBuf) {
        // We will be using the fact that <payload size> = <start of last
        // packet> - 15
        ByteBuffer dup = readBuf.duplicate();
        int payloadSize = 0;
        NIOUtils.skip(dup, 5);
        while (dup.hasRemaining()) {
            payloadSize = ((payloadSize & 0xffff) << 8) | (dup.get() & 0xff);
            int pointerPos = dup.position() + 7 + payloadSize;
            if (dup.position() >= 8 && pointerPos < dup.limit() - 4 && dup.getInt(pointerPos) - payloadSize == 11) {
                readBuf.position(dup.position() - 8);
                return true;
            }
        }
        return false;
    }

    /**
     * Searching for the next tag in a file after corrupt segment
     *
     * @return
     * @throws IOException
     */
    public boolean repositionFile() throws IOException {
        int payloadSize = 0;
        for (int i = 0; i < REPOSITION_BUFFER_READS; i++) {
            while (readBuf.hasRemaining()) {
                payloadSize = ((payloadSize & 0xffff) << 8) | (readBuf.get() & 0xff);
                int pointerPos = readBuf.position() + 7 + payloadSize;
                if (readBuf.position() >= 8 && pointerPos < readBuf.limit() - 4
                        && readBuf.getInt(pointerPos) - payloadSize == 11) {
                    readBuf.position(readBuf.position() - 8);
                    return true;
                }
            }
            initialRead(ch);
            if (!readBuf.hasRemaining())
                break;
        }
        return false;
    }
}


package org.jcodec.common.dct;
import java.nio.ShortBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org )
 * This software is distributed under FreeBSD License
 *
 * @author Alex Zhukov
 *
 */
public class FfmpegIntDct {
    private static final int DCTSIZE = 8;
    private static final int DCTSIZE_6 = DCTSIZE * 6;
    private static final int DCTSIZE_5 = DCTSIZE * 5;
    private static final int DCTSIZE_4 = DCTSIZE * 4;
    private static final int DCTSIZE_3 = DCTSIZE * 3;
    private static final int DCTSIZE_2 = DCTSIZE * 2;
    private static final int DCTSIZE_1 = DCTSIZE * 1;
    private static final int DCTSIZE_7 = DCTSIZE * 7;
    private static final int DCTSIZE_0 = DCTSIZE * 0;
    private static final int PASS1_BITS = 2;
    private static final int CONST_BITS = 13;
    private static final int D1 = CONST_BITS - PASS1_BITS;
    private static final int D2 = CONST_BITS + PASS1_BITS + 3;
    private static final int ONEHALF_11 = (1 << (D1 - 1));
    private static final int ONEHALF_18 = (1 << (D2 - 1));

    public short[] decode(short[] orig) {
        ShortBuffer data = ShortBuffer.wrap(orig);
        pass1(data);
        pass2(data);
        return orig;
    }

    private static ShortBuffer advance(ShortBuffer dataptr, int size) {
        dataptr.position(dataptr.position() + size);
        return dataptr.slice();
    }

    /*
     * Unlike our decoder where we approximate the FIXes, we need to use exact
     * ones here or successive P-frames will drift too much with Reference frame
     * coding
     */
    private final static short FIX_0_211164243 = 1730;
    private final static short FIX_0_275899380 = 2260;
    private final static short FIX_0_298631336 = 2446;
    private final static short FIX_0_390180644 = 3196;
    private final static short FIX_0_509795579 = 4176;
    private final static short FIX_0_541196100 = 4433;
    private final static short FIX_0_601344887 = 4926;
    private final static short FIX_0_765366865 = 6270;
    private final static short FIX_0_785694958 = 6436;
    private final static short FIX_0_899976223 = 7373;
    private final static short FIX_1_061594337 = 8697;
    private final static short FIX_1_111140466 = 9102;
    private final static short FIX_1_175875602 = 9633;
    private final static short FIX_1_306562965 = 10703;
    private final static short FIX_1_387039845 = 11363;
    private final static short FIX_1_451774981 = 11893;
    private final static short FIX_1_501321110 = 12299;
    private final static short FIX_1_662939225 = 13623;
    private final static short FIX_1_847759065 = 15137;
    private final static short FIX_1_961570560 = 16069;
    private final static short FIX_2_053119869 = 16819;
    private final static short FIX_2_172734803 = 17799;
    private final static short FIX_2_562915447 = 20995;
    private final static short FIX_3_072711026 = 25172;

    /* Pass 1: process rows. */
    /* Note results are scaled up by sqrt(8) compared to a true IDCT; */
    /* furthermore, we scale the results by 2PASS1_BITS. */
    private final static void pass1(ShortBuffer data) {
        int z1;
        int tmp2;
        int tmp3;
        int tmp0;
        int tmp1;
        int tmp10;
        int tmp13;
        int tmp11;
        int tmp12;
        int z2;
        int z3;
        int z4;
        int z5;

        ShortBuffer dataptr = data.duplicate();

        for (int rowctr = DCTSIZE - 1; rowctr >= 0; rowctr--) {
            /*
             * Due to quantization, we will usually find that many of the input
             * coefficients are zero, especially the AC terms. We can exploit
             * this by short-circuiting the IDCT calculation for any row in
             * which all the AC terms are zero. In that case each output is
             * equal to the DC coefficient (with scale factor as needed). With
             * typical images and quantization tables, half or more of the row
             * DCT calculations can be simplified this way.
             */

            // register int *idataptr = (int*)dataptr;
            /*
             * WARNING: we do the same permutation as MMX idct to simplify the
             * video core
             */
            int d0 = dataptr.get(0);
            int d2 = dataptr.get(1);
            int d4 = dataptr.get(2);
            int d6 = dataptr.get(3);
            int d1 = dataptr.get(4);
            int d3 = dataptr.get(5);
            int d5 = dataptr.get(6);
            int d7 = dataptr.get(7);

            if ((d1 | d2 | d3 | d4 | d5 | d6 | d7) == 0) {
                /* AC terms all zero */
                if (d0 != 0) {
                    /* Compute a 32 bit value to assign. */
                    int dcval = (int) (d0 << PASS1_BITS);
                    for (int i = 0; i < 8; i++) {
                        dataptr.put(i, (short) dcval);
                    }
                }
                /*
                 * advance pointer to next row
                 */
                dataptr = advance(dataptr, DCTSIZE);
                continue;
            }

            /* Even part: reverse the even part of the forward DCT. */
            /* The rotator is sqrt(2)c(-6). */
            if (d6 != 0) {
                if (d2 != 0) {
                    /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
                    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
                    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
                    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                } else {
                    /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
                    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
                    tmp3 = MULTIPLY(d6, FIX_0_541196100);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                }
            } else {
                if (d2 != 0) {
                    /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
                    tmp2 = MULTIPLY(d2, FIX_0_541196100);
                    tmp3 = MULTIPLY(d2, FIX_1_306562965);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                } else {
                    /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
                    tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
                    tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
                }
            }

            /*
             * Odd part per figure 8; the matrix is unitary and hence its
             * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
             */

            if (d7 != 0) {
                if (d5 != 0) {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(z3 + z4, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z5 = MULTIPLY(z3 + d5, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(d7 + z4, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 = z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
                            tmp0 = MULTIPLY(-d7, FIX_0_601344887);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            tmp1 = MULTIPLY(-d5, FIX_0_509795579);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);
                            z5 = MULTIPLY(d5 + d7, FIX_1_175875602);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z3;
                            tmp1 += z4;
                            tmp2 = z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z3 = d7 + d3;
                            z5 = MULTIPLY(z3 + d1, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-d3, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-d1, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 = z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
                            z3 = d7 + d3;

                            tmp0 = MULTIPLY(-d7, FIX_0_601344887);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            tmp2 = MULTIPLY(d3, FIX_0_509795579);
                            z2 = MULTIPLY(-d3, FIX_2_562915447);
                            z5 = MULTIPLY(z3, FIX_1_175875602);
                            z3 = MULTIPLY(-z3, FIX_0_785694958);

                            tmp0 += z3;
                            tmp1 = z2 + z5;
                            tmp2 += z3;
                            tmp3 = z1 + z5;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z5 = MULTIPLY(z1, FIX_1_175875602);

                            z1 = MULTIPLY(z1, FIX_0_275899380);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            tmp0 = MULTIPLY(-d7, FIX_1_662939225);
                            z4 = MULTIPLY(-d1, FIX_0_390180644);
                            tmp3 = MULTIPLY(d1, FIX_1_111140466);

                            tmp0 += z1;
                            tmp1 = z4 + z5;
                            tmp2 = z3 + z5;
                            tmp3 += z1;
                        } else {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
                            tmp0 = MULTIPLY(-d7, FIX_1_387039845);
                            tmp1 = MULTIPLY(d7, FIX_1_175875602);
                            tmp2 = MULTIPLY(-d7, FIX_0_785694958);
                            tmp3 = MULTIPLY(d7, FIX_0_275899380);
                        }
                    }
                }
            } else {
                if (d5 != 0) {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(d3 + z4, FIX_1_175875602);

                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-d1, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-d3, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 = z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;

                            z5 = MULTIPLY(z2, FIX_1_175875602);
                            tmp1 = MULTIPLY(d5, FIX_1_662939225);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);
                            z2 = MULTIPLY(-z2, FIX_1_387039845);
                            tmp2 = MULTIPLY(d3, FIX_1_111140466);
                            z3 = MULTIPLY(-d3, FIX_1_961570560);

                            tmp0 = z3 + z5;
                            tmp1 += z2;
                            tmp2 += z2;
                            tmp3 = z4 + z5;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
                            z4 = d5 + d1;

                            z5 = MULTIPLY(z4, FIX_1_175875602);
                            z1 = MULTIPLY(-d1, FIX_0_899976223);
                            tmp3 = MULTIPLY(d1, FIX_0_601344887);
                            tmp1 = MULTIPLY(-d5, FIX_0_509795579);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z4 = MULTIPLY(z4, FIX_0_785694958);

                            tmp0 = z1 + z5;
                            tmp1 += z4;
                            tmp2 = z2 + z5;
                            tmp3 += z4;
                        } else {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
                            tmp0 = MULTIPLY(d5, FIX_1_175875602);
                            tmp1 = MULTIPLY(d5, FIX_0_275899380);
                            tmp2 = MULTIPLY(-d5, FIX_1_387039845);
                            tmp3 = MULTIPLY(d5, FIX_0_785694958);
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
                            z5 = d1 + d3;
                            tmp3 = MULTIPLY(d1, FIX_0_211164243);
                            tmp2 = MULTIPLY(-d3, FIX_1_451774981);
                            z1 = MULTIPLY(d1, FIX_1_061594337);
                            z2 = MULTIPLY(-d3, FIX_2_172734803);
                            z4 = MULTIPLY(z5, FIX_0_785694958);
                            z5 = MULTIPLY(z5, FIX_1_175875602);

                            tmp0 = z1 - z4;
                            tmp1 = z2 + z4;
                            tmp2 += z5;
                            tmp3 += z5;
                        } else {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
                            tmp0 = MULTIPLY(-d3, FIX_0_785694958);
                            tmp1 = MULTIPLY(-d3, FIX_1_387039845);
                            tmp2 = MULTIPLY(-d3, FIX_0_275899380);
                            tmp3 = MULTIPLY(d3, FIX_1_175875602);
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = MULTIPLY(d1, FIX_0_275899380);
                            tmp1 = MULTIPLY(d1, FIX_0_785694958);
                            tmp2 = MULTIPLY(d1, FIX_1_175875602);
                            tmp3 = MULTIPLY(d1, FIX_1_387039845);
                        } else {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = tmp1 = tmp2 = tmp3 = 0;
                        }
                    }
                }
            }
            /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */

            dataptr.put(0, DESCALE11(tmp10 + tmp3));
            dataptr.put(7, DESCALE11(tmp10 - tmp3));
            dataptr.put(1, DESCALE11(tmp11 + tmp2));
            dataptr.put(6, DESCALE11(tmp11 - tmp2));
            dataptr.put(2, DESCALE11(tmp12 + tmp1));
            dataptr.put(5, DESCALE11(tmp12 - tmp1));
            dataptr.put(3, DESCALE11(tmp13 + tmp0));
            dataptr.put(4, DESCALE11(tmp13 - tmp0));

            dataptr = advance(dataptr, DCTSIZE); /* advance pointer to next row */
        }

    }

    private static int MULTIPLY(int x, short y) {
        return y * (short) x;
    }

    /**
     * Perform the inverse DCT on one block of coefficients.
     */
    private final static void pass2(ShortBuffer data) {
        int tmp0, tmp1, tmp2, tmp3;
        int tmp10, tmp11, tmp12, tmp13;
        int z1, z2, z3, z4, z5;
        int d0, d1, d2, d3, d4, d5, d6, d7;
        ShortBuffer dataptr = data.duplicate();

        /* Pass 2: process columns. */
        /* Note that we must descale the results by a factor of 8 == 23, */
        /* and also undo the PASS1_BITS scaling. */

        for (int rowctr = DCTSIZE - 1; rowctr >= 0; rowctr--) {
            /*
             * Columns of zeroes can be exploited in the same way as we did with
             * rows. However, the row calculation has created many nonzero AC
             * terms, so the simplification applies less often (typically 5% to
             * 10% of the time). On machines with very fast multiplication, it's
             * possible that the test takes more time than it's worth. In that
             * case this section may be commented out.
             */

            d0 = dataptr.get(DCTSIZE_0);
            d1 = dataptr.get(DCTSIZE_1);
            d2 = dataptr.get(DCTSIZE_2);
            d3 = dataptr.get(DCTSIZE_3);
            d4 = dataptr.get(DCTSIZE_4);
            d5 = dataptr.get(DCTSIZE_5);
            d6 = dataptr.get(DCTSIZE_6);
            d7 = dataptr.get(DCTSIZE_7);

            /* Even part: reverse the even part of the forward DCT. */
            /* The rotator is sqrt(2)c(-6). */
            if (d6 != 0) {
                if (d2 != 0) {
                    /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */
                    z1 = MULTIPLY(d2 + d6, FIX_0_541196100);
                    tmp2 = z1 + MULTIPLY(-d6, FIX_1_847759065);
                    tmp3 = z1 + MULTIPLY(d2, FIX_0_765366865);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                } else {
                    /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */
                    tmp2 = MULTIPLY(-d6, FIX_1_306562965);
                    tmp3 = MULTIPLY(d6, FIX_0_541196100);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                }
            } else {
                if (d2 != 0) {
                    /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */
                    tmp2 = MULTIPLY(d2, FIX_0_541196100);
                    tmp3 = MULTIPLY(d2, FIX_1_306562965);

                    tmp0 = (d0 + d4) << CONST_BITS;
                    tmp1 = (d0 - d4) << CONST_BITS;

                    tmp10 = tmp0 + tmp3;
                    tmp13 = tmp0 - tmp3;
                    tmp11 = tmp1 + tmp2;
                    tmp12 = tmp1 - tmp2;
                } else {
                    /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */
                    tmp10 = tmp13 = (d0 + d4) << CONST_BITS;
                    tmp11 = tmp12 = (d0 - d4) << CONST_BITS;
                }
            }

            /*
             * Odd part per figure 8; the matrix is unitary and hence its
             * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively.
             */
            if (d7 != 0) {
                if (d5 != 0) {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(z3 + z4, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */
                            z1 = d7;
                            z2 = d5 + d3;
                            z3 = d7 + d3;
                            z5 = MULTIPLY(z3 + d5, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */
                            z1 = d7 + d1;
                            z2 = d5;
                            z3 = d7;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(z3 + z4, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 = z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */
                            tmp0 = MULTIPLY(-d7, FIX_0_601344887);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            tmp1 = MULTIPLY(-d5, FIX_0_509795579);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);
                            z5 = MULTIPLY(d5 + d7, FIX_1_175875602);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z3;
                            tmp1 += z4;
                            tmp2 = z2 + z3;
                            tmp3 = z1 + z4;
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z3 = d7 + d3;
                            z5 = MULTIPLY(z3 + d1, FIX_1_175875602);

                            tmp0 = MULTIPLY(d7, FIX_0_298631336);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-z1, FIX_0_899976223);
                            z2 = MULTIPLY(-d3, FIX_2_562915447);
                            z3 = MULTIPLY(-z3, FIX_1_961570560);
                            z4 = MULTIPLY(-d1, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 += z1 + z3;
                            tmp1 = z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */
                            z3 = d7 + d3;

                            tmp0 = MULTIPLY(-d7, FIX_0_601344887);
                            z1 = MULTIPLY(-d7, FIX_0_899976223);
                            tmp2 = MULTIPLY(d3, FIX_0_509795579);
                            z2 = MULTIPLY(-d3, FIX_2_562915447);
                            z5 = MULTIPLY(z3, FIX_1_175875602);
                            z3 = MULTIPLY(-z3, FIX_0_785694958);

                            tmp0 += z3;
                            tmp1 = z2 + z5;
                            tmp2 += z3;
                            tmp3 = z1 + z5;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */
                            z1 = d7 + d1;
                            z5 = MULTIPLY(z1, FIX_1_175875602);

                            z1 = MULTIPLY(z1, FIX_0_275899380);
                            z3 = MULTIPLY(-d7, FIX_1_961570560);
                            tmp0 = MULTIPLY(-d7, FIX_1_662939225);
                            z4 = MULTIPLY(-d1, FIX_0_390180644);
                            tmp3 = MULTIPLY(d1, FIX_1_111140466);

                            tmp0 += z1;
                            tmp1 = z4 + z5;
                            tmp2 = z3 + z5;
                            tmp3 += z1;
                        } else {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */
                            tmp0 = MULTIPLY(-d7, FIX_1_387039845);
                            tmp1 = MULTIPLY(d7, FIX_1_175875602);
                            tmp2 = MULTIPLY(-d7, FIX_0_785694958);
                            tmp3 = MULTIPLY(d7, FIX_0_275899380);
                        }
                    }
                }
            } else {
                if (d5 != 0) {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;
                            z4 = d5 + d1;
                            z5 = MULTIPLY(d3 + z4, FIX_1_175875602);

                            tmp1 = MULTIPLY(d5, FIX_2_053119869);
                            tmp2 = MULTIPLY(d3, FIX_3_072711026);
                            tmp3 = MULTIPLY(d1, FIX_1_501321110);
                            z1 = MULTIPLY(-d1, FIX_0_899976223);
                            z2 = MULTIPLY(-z2, FIX_2_562915447);
                            z3 = MULTIPLY(-d3, FIX_1_961570560);
                            z4 = MULTIPLY(-z4, FIX_0_390180644);

                            z3 += z5;
                            z4 += z5;

                            tmp0 = z1 + z3;
                            tmp1 += z2 + z4;
                            tmp2 += z2 + z3;
                            tmp3 += z1 + z4;
                        } else {
                            /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */
                            z2 = d5 + d3;

                            z5 = MULTIPLY(z2, FIX_1_175875602);
                            tmp1 = MULTIPLY(d5, FIX_1_662939225);
                            z4 = MULTIPLY(-d5, FIX_0_390180644);
                            z2 = MULTIPLY(-z2, FIX_1_387039845);
                            tmp2 = MULTIPLY(d3, FIX_1_111140466);
                            z3 = MULTIPLY(-d3, FIX_1_961570560);

                            tmp0 = z3 + z5;
                            tmp1 += z2;
                            tmp2 += z2;
                            tmp3 = z4 + z5;
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */
                            z4 = d5 + d1;

                            z5 = MULTIPLY(z4, FIX_1_175875602);
                            z1 = MULTIPLY(-d1, FIX_0_899976223);
                            tmp3 = MULTIPLY(d1, FIX_0_601344887);
                            tmp1 = MULTIPLY(-d5, FIX_0_509795579);
                            z2 = MULTIPLY(-d5, FIX_2_562915447);
                            z4 = MULTIPLY(z4, FIX_0_785694958);

                            tmp0 = z1 + z5;
                            tmp1 += z4;
                            tmp2 = z2 + z5;
                            tmp3 += z4;
                        } else {
                            /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */
                            tmp0 = MULTIPLY(d5, FIX_1_175875602);
                            tmp1 = MULTIPLY(d5, FIX_0_275899380);
                            tmp2 = MULTIPLY(-d5, FIX_1_387039845);
                            tmp3 = MULTIPLY(d5, FIX_0_785694958);
                        }
                    }
                } else {
                    if (d3 != 0) {
                        if (d1 != 0) {
                            /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */
                            z5 = d1 + d3;
                            tmp3 = MULTIPLY(d1, FIX_0_211164243);
                            tmp2 = MULTIPLY(-d3, FIX_1_451774981);
                            z1 = MULTIPLY(d1, FIX_1_061594337);
                            z2 = MULTIPLY(-d3, FIX_2_172734803);
                            z4 = MULTIPLY(z5, FIX_0_785694958);
                            z5 = MULTIPLY(z5, FIX_1_175875602);

                            tmp0 = z1 - z4;
                            tmp1 = z2 + z4;
                            tmp2 += z5;
                            tmp3 += z5;
                        } else {
                            /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */
                            tmp0 = MULTIPLY(-d3, FIX_0_785694958);
                            tmp1 = MULTIPLY(-d3, FIX_1_387039845);
                            tmp2 = MULTIPLY(-d3, FIX_0_275899380);
                            tmp3 = MULTIPLY(d3, FIX_1_175875602);
                        }
                    } else {
                        if (d1 != 0) {
                            /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = MULTIPLY(d1, FIX_0_275899380);
                            tmp1 = MULTIPLY(d1, FIX_0_785694958);
                            tmp2 = MULTIPLY(d1, FIX_1_175875602);
                            tmp3 = MULTIPLY(d1, FIX_1_387039845);
                        } else {
                            /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */
                            tmp0 = tmp1 = tmp2 = tmp3 = 0;
                        }
                    }
                }
            }

            /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */

            dataptr.put(DCTSIZE_0, DESCALE18(tmp10 + tmp3));
            dataptr.put(DCTSIZE_7, DESCALE18(tmp10 - tmp3));
            dataptr.put(DCTSIZE_1, DESCALE18(tmp11 + tmp2));
            dataptr.put(DCTSIZE_6, DESCALE18(tmp11 - tmp2));
            dataptr.put(DCTSIZE_2, DESCALE18(tmp12 + tmp1));
            dataptr.put(DCTSIZE_5, DESCALE18(tmp12 - tmp1));
            dataptr.put(DCTSIZE_3, DESCALE18(tmp13 + tmp0));
            dataptr.put(DCTSIZE_4, DESCALE18(tmp13 - tmp0));

            dataptr = advance(dataptr, 1); /* advance pointer to next column */
        }
    }

    private final static int DESCALE(int x, int n) {
        return ((x) + (1 << ((n) - 1))) >> n;
    }

    private final static short DESCALE11(int x) {
        return (short) ((x + ONEHALF_11) >> 11);
    }

    private final static short DESCALE18(int x) {
        return (short) ((x + ONEHALF_18) >> 18);
    }
}


package org.jcodec.codecs.common.biari;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * H264 CABAC M-Coder ( decoder module )
 *
 * @author The JCodec project
 *
 */
public class MDecoder {

    private ByteBuffer _in;
    private int range;
    private int code;
    private int nBitsPending;
    private int[][] cm;

    public MDecoder(ByteBuffer _in, int[][] cm) {
        this._in = _in;
        this.range = 510;
        this.cm = cm;

        initCodeRegister();
    }

    /**
     * Initializes code register. Loads 9 bits from the stream into working area
     * of code register ( bits 8 - 16) leaving 7 bits in the pending area of
     * code register (bits 0 - 7)
     *
     * @throws IOException
     */
    protected void initCodeRegister() {
        readOneByte();
        if (nBitsPending != 8)
            throw new RuntimeException("Empty stream");
        code <<= 8;
        readOneByte();
        code <<= 1;
        nBitsPending -= 9;
    }

    protected void readOneByte() {
        if (!_in.hasRemaining())
            return;
        int b = _in.get() & 0xff;
        code |= b;
        nBitsPending += 8;
    }

    /**
     * Decodes one bin from arithmetice code word
     *
     * @param cm
     * @return
     * @throws IOException
     */
    public int decodeBin(int m) {
        int bin;

        int qIdx = (range >> 6) & 0x3;
        int rLPS = MConst.rangeLPS[qIdx][cm[0][m]];
        range -= rLPS;
        int rs8 = range << 8;

        if (code < rs8) {
            // MPS
            if (cm[0][m] < 62)
                cm[0][m]++;

            renormalize();

            bin = cm[1][m];
        } else {
            // LPS
            range = rLPS;
            code -= rs8;

            renormalize();

            bin = 1 - cm[1][m];

            if (cm[0][m] == 0)
                cm[1][m] = 1 - cm[1][m];

            cm[0][m] = MConst.transitLPS[cm[0][m]];
        }

//        System.out.println("CABAC BIT [" + m + "]: " + bin);
        return bin;
    }

    /**
     * Special decoding process for 'end of slice' flag. Uses probability state
     * 63.
     *
     * @param cm
     * @return
     * @throws IOException
     */
    public int decodeFinalBin() {
        range -= 2;

        if (code < (range << 8)) {
            renormalize();
//            System.out.println("CABAC BIT [-2]: 0");
            return 0;
        } else {
//            System.out.println("CABAC BIT [-2]: 1");
            return 1;
        }
    }

    /**
     * Special decoding process for symbols with uniform distribution
     *
     * @return
     * @throws IOException
     */
    public int decodeBinBypass() {
        code <<= 1;

        --nBitsPending;
        if (nBitsPending <= 0)
            readOneByte();

        int tmp = code - (range << 8);
        if (tmp < 0) {
//            System.out.println("CABAC BIT [-1]: 0");
            return 0;
        } else {
//            System.out.println("CABAC BIT [-1]: 1");
            code = tmp;
            return 1;
        }
    }

    /**
     * Shifts the current interval to either 1/2 or 0 (code = (code << 1) &
     * 0x1ffff) and scales it by 2 (range << 1).
     *
     * Reads new byte from the input stream into code value if there are no more
     * bits pending
     *
     * @throws IOException
     */
    private void renormalize() {
        while (range < 256) {
            range <<= 1;
            code <<= 1;
            code &= 0x1ffff;

            --nBitsPending;
            if (nBitsPending <= 0)
                readOneByte();
        }
    }
}


package org.jcodec.codecs.mpeg12;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * This class is part of JCodec ( www.jcodec.org ) This software is distributed
 * under FreeBSD License
 *
 * @author The JCodec project
 *
 */
public class MPEGUtil {

    /**
     * Finds next MPEG bitstream marker 0x000001xx and returns the data that
     * preceeds it as a ByteBuffer slice
     *
     * Segment byte order is always little endian
     *
     * @param buf
     * @return
     */
    public static final ByteBuffer gotoNextMarker(ByteBuffer buf) {
        return gotoMarker(buf, 0, 0x100, 0x1ff);
    }

    /**
     * Finds next Nth MPEG bitstream marker 0x000001xx and returns the data that
     * preceeds it as a ByteBuffer slice
     *
     * Segment byte order is always little endian
     *
     * @param buf
     * @return
     */
    public static final ByteBuffer gotoMarker(ByteBuffer buf, int n, int mmin, int mmax) {
        if (!buf.hasRemaining())
            return null;

        int from = buf.position();
        ByteBuffer result = buf.slice();
        result.order(ByteOrder.BIG_ENDIAN);

        int val = 0xffffffff;
        while (buf.hasRemaining()) {
            val = (val << 8) | (buf.get() & 0xff);
            if (val >= mmin && val <= mmax) {
                if (n == 0) {
                    buf.position(buf.position() - 4);
                    result.limit(buf.position() - from);
                    break;
                }
                --n;
            }
        }
        return result;
    }

    /**
     * Returns next segment between two MPEG marker
     *
     * i.e. searches for the next marker if the stream is not at the marker
     * boundary already
     *
     * @param buf
     * @return
     */
    public static final ByteBuffer nextSegment(ByteBuffer buf) {
        gotoMarker(buf, 0, 0x100, 0x1ff);
        return gotoMarker(buf, 1, 0x100, 0x1ff);
    }
}


package net.sourceforge.jaad.aac.sbr;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 *
 * @author in-somnia
 */
class TFGrid implements SBRConstants {

	/* function constructs new time border vector */
	/* first build into temp vector to be able to use previous vector on error */
	public static int envelope_time_border_vector(SBR sbr, int ch) {
		int l, border, temp;
		int[] t_E_temp = new int[6];

		t_E_temp[0] = sbr.rate*sbr.abs_bord_lead[ch];
		t_E_temp[sbr.L_E[ch]] = sbr.rate*sbr.abs_bord_trail[ch];

		switch(sbr.bs_frame_class[ch]) {
			case FIXFIX:
				switch(sbr.L_E[ch]) {
					case 4:
						temp = (sbr.numTimeSlots/4);
						t_E_temp[3] = sbr.rate*3*temp;
						t_E_temp[2] = sbr.rate*2*temp;
						t_E_temp[1] = sbr.rate*temp;
						break;
					case 2:
						t_E_temp[1] = sbr.rate*(sbr.numTimeSlots/2);
						break;
					default:
						break;
				}
				break;

			case FIXVAR:
				if(sbr.L_E[ch]>1) {
					int i = sbr.L_E[ch];
					border = sbr.abs_bord_trail[ch];

					for(l = 0; l<(sbr.L_E[ch]-1); l++) {
						if(border<sbr.bs_rel_bord[ch][l])
							return 1;

						border -= sbr.bs_rel_bord[ch][l];
						t_E_temp[--i] = sbr.rate*border;
					}
				}
				break;

			case VARFIX:
				if(sbr.L_E[ch]>1) {
					int i = 1;
					border = sbr.abs_bord_lead[ch];

					for(l = 0; l<(sbr.L_E[ch]-1); l++) {
						border += sbr.bs_rel_bord[ch][l];

						if(sbr.rate*border+sbr.tHFAdj>sbr.numTimeSlotsRate+sbr.tHFGen)
							return 1;

						t_E_temp[i++] = sbr.rate*border;
					}
				}
				break;

			case VARVAR:
				if(sbr.bs_num_rel_0[ch]!=0) {
					int i = 1;
					border = sbr.abs_bord_lead[ch];

					for(l = 0; l<sbr.bs_num_rel_0[ch]; l++) {
						border += sbr.bs_rel_bord_0[ch][l];

						if(sbr.rate*border+sbr.tHFAdj>sbr.numTimeSlotsRate+sbr.tHFGen)
							return 1;

						t_E_temp[i++] = sbr.rate*border;
					}
				}

				if(sbr.bs_num_rel_1[ch]!=0) {
					int i = sbr.L_E[ch];
					border = sbr.abs_bord_trail[ch];

					for(l = 0; l<sbr.bs_num_rel_1[ch]; l++) {
						if(border<sbr.bs_rel_bord_1[ch][l])
							return 1;

						border -= sbr.bs_rel_bord_1[ch][l];
						t_E_temp[--i] = sbr.rate*border;
					}
				}
				break;
		}

		/* no error occured, we can safely use this t_E vector */
		for(l = 0; l<6; l++) {
			sbr.t_E[ch][l] = t_E_temp[l];
		}

		return 0;
	}

	public static void noise_floor_time_border_vector(SBR sbr, int ch) {
		sbr.t_Q[ch][0] = sbr.t_E[ch][0];

		if(sbr.L_E[ch]==1) {
			sbr.t_Q[ch][1] = sbr.t_E[ch][1];
			sbr.t_Q[ch][2] = 0;
		}
		else {
			int index = middleBorder(sbr, ch);
			sbr.t_Q[ch][1] = sbr.t_E[ch][index];
			sbr.t_Q[ch][2] = sbr.t_E[ch][sbr.L_E[ch]];
		}
	}

	private static int middleBorder(SBR sbr, int ch) {
		int retval = 0;

		switch(sbr.bs_frame_class[ch]) {
			case FIXFIX:
				retval = sbr.L_E[ch]/2;
				break;
			case VARFIX:
				if(sbr.bs_pointer[ch]==0)
					retval = 1;
				else if(sbr.bs_pointer[ch]==1)
					retval = sbr.L_E[ch]-1;
				else
					retval = sbr.bs_pointer[ch]-1;
				break;
			case FIXVAR:
			case VARVAR:
				if(sbr.bs_pointer[ch]>1)
					retval = sbr.L_E[ch]+1-sbr.bs_pointer[ch];
				else
					retval = sbr.L_E[ch]-1;
				break;
		}

		return (retval>0) ? retval : 0;
	}

}


package net.sourceforge.jaad.aac.ps;

/**
 * This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
 * under the Public Domain license. Code changes provided by the JCodec project
 * are distributed under FreeBSD license.
 *
 * @author in-somnia
 */
class PSFilterbank implements PSTables {

	private int frame_len;
	private int[] resolution20;
	private int[] resolution34;

	private float[][] work;
	private float[][][] buffer;
	private float[][][] temp;

	PSFilterbank(int numTimeSlotsRate) {
	    this.resolution20 = new int[3];
	    this.resolution34 = new int[5];

		int i;

		this.resolution34[0] = 12;
		this.resolution34[1] = 8;
		this.resolution34[2] = 4;
		this.resolution34[3] = 4;
		this.resolution34[4] = 4;

		this.resolution20[0] = 8;
		this.resolution20[1] = 2;
		this.resolution20[2] = 2;

		this.frame_len = numTimeSlotsRate;

		this.work = new float[(this.frame_len+12)][2];

		this.buffer = new float[5][2][2];

		temp = new float[frame_len][12][2];
	}

	void hybrid_analysis(float[][][] X, float[][][] X_hybrid, boolean use34, int numTimeSlotsRate) {
		int k, n, band;
		int offset = 0;
		int qmf_bands = (use34) ? 5 : 3;
		int[] resolution = (use34) ? this.resolution34 : this.resolution20;

		for(band = 0; band<qmf_bands; band++) {
			/* build working buffer */
			//memcpy(this.work, this.buffer[band], 12*sizeof(qmf_t));
			for(int i = 0; i<12; i++) {
				work[i][0] = buffer[band][i][0];
				work[i][1] = buffer[band][i][1];
			}

			/* add new samples */
			for(n = 0; n<this.frame_len; n++) {
				this.work[12+n][0] = X[n+6 /*delay*/][band][0];
				this.work[12+n][0] = X[n+6 /*delay*/][band][0];
			}

			/* store samples */
			//memcpy(this.buffer[band], this.work+this.frame_len, 12*sizeof(qmf_t));
			for(int i = 0; i<12; i++) {
				buffer[band][i][0] = work[frame_len+i][0];
				buffer[band][i][1] = work[frame_len+i][1];
			}

			switch(resolution[band]) {
				case 2:
					/* Type B real filter, Q[p] = 2 */
					channel_filter2(this.frame_len, p2_13_20, this.work, this.temp);
					break;
				case 4:
					/* Type A complex filter, Q[p] = 4 */
					channel_filter4(this.frame_len, p4_13_34, this.work, this.temp);
					break;
				case 8:
					/* Type A complex filter, Q[p] = 8 */
					channel_filter8(this.frame_len, (use34) ? p8_13_34 : p8_13_20,
						this.work, this.temp);
					break;
				case 12:
					/* Type A complex filter, Q[p] = 12 */
					channel_filter12(this.frame_len, p12_13_34, this.work, this.temp);
					break;
			}

			for(n = 0; n<this.frame_len; n++) {
				for(k = 0; k<resolution[band]; k++) {
					X_hybrid[n][offset+k][0] = this.temp[n][k][0];
					X_hybrid[n][offset+k][1] = this.temp[n][k][1];
				}
			}
			offset += resolution[band];
		}

		/* group hybrid channels */
		if(!use34) {
			for(n = 0; n<numTimeSlotsRate; n++) {
				X_hybrid[n][3][0] += X_hybrid[n][4][0];
				X_hybrid[n][3][1] += X_hybrid[n][4][1];
				X_hybrid[n][4][0] = 0;
				X_hybrid[n][4][1] = 0;

				X_hybrid[n][2][0] += X_hybrid[n][5][0];
				X_hybrid[n][2][1] += X_hybrid[n][5][1];
				X_hybrid[n][5][0] = 0;
				X_hybrid[n][5][1] = 0;
			}
		}
	}

	/* real filter, size 2 */
	static void channel_filter2(int frame_len, float[] filter,
		float[][] buffer, float[][][] X_hybrid) {
		int i;

		for(i = 0; i<frame_len; i++) {
			float r0 = (filter[0]*(buffer[0+i][0]+buffer[12+i][0]));
			float r1 = (filter[1]*(buffer[1+i][0]+buffer[11+i][0]));
			float r2 = (filter[2]*(buffer[2+i][0]+buffer[10+i][0]));
			float r3 = (filter[3]*(buffer[3+i][0]+buffer[9+i][0]));
			float r4 = (filter[4]*(buffer[4+i][0]+buffer[8+i][0]));
			float r5 = (filter[5]*(buffer[5+i][0]+buffer[7+i][0]));
			float r6 = (filter[6]*buffer[6+i][0]);
			float i0 = (filter[0]*(buffer[0+i][1]+buffer[12+i][1]));
			float i1 = (filter[1]*(buffer[1+i][1]+buffer[11+i][1]));
			float i2 = (filter[2]*(buffer[2+i][1]+buffer[10+i][1]));
			float i3 = (filter[3]*(buffer[3+i][1]+buffer[9+i][1]));
			float i4 = (filter[4]*(buffer[4+i][1]+buffer[8+i][1]));
			float i5 = (filter[5]*(buffer[5+i][1]+buffer[7+i][1]));
			float i6 = (filter[6]*buffer[6+i][1]);

			/* q = 0 */
			X_hybrid[i][0][0] = r0+r1+r2+r3+r4+r5+r6;
			X_hybrid[i][0][1] = i0+i1+i2+i3+i4+i5+i6;

			/* q = 1 */
			X_hybrid[i][1][0] = r0-r1+r2-r3+r4-r5+r6;
			X_hybrid[i][1][1] = i0-i1+i2-i3+i4-i5+i6;
		}
	}

	/* complex filter, size 4 */
	static void channel_filter4(int frame_len, float[] filter,
		float[][] buffer, float[][][] X_hybrid) {
		int i;
		float[] input_re1 = new float[2], input_re2 = new float[2];
		float[] input_im1 = new float[2], input_im2 = new float[2];

		for(i = 0; i<frame_len; i++) {
			input_re1[0] = -(filter[2]*(buffer[i+2][0]+buffer[i+10][0]))
				+(filter[6]*buffer[i+6][0]);
			input_re1[1] = (-0.70710678118655f
				*((filter[1]*(buffer[i+1][0]+buffer[i+11][0]))
				+(filter[3]*(buffer[i+3][0]+buffer[i+9][0]))
				-(filter[5]*(buffer[i+5][0]+buffer[i+7][0]))));

			input_im1[0] = (filter[0]*(buffer[i+0][1]-buffer[i+12][1]))
				-(filter[4]*(buffer[i+4][1]-buffer[i+8][1]));
			input_im1[1] = (0.70710678118655f
				*((filter[1]*(buffer[i+1][1]-buffer[i+11][1]))
				-(filter[3]*(buffer[i+3][1]-buffer[i+9][1]))
				-(filter[5]*(buffer[i+5][1]-buffer[i+7][1]))));

			input_re2[0] = (filter[0]*(buffer[i+0][0]-buffer[i+12][0]))
				-(filter[4]*(buffer[i+4][0]-buffer[i+8][0]));
			input_re2[1] = (0.70710678118655f
				*((filter[1]*(buffer[i+1][0]-buffer[i+11][0]))
				-(filter[3]*(buffer[i+3][0]-buffer[i+9][0]))
				-(filter[5]*(buffer[i+5][0]-buffer[i+7][0]))));

			input_im2[0] = -(filter[2]*(buffer[i+2][1]+buffer[i+10][1]))
				+(filter[6]*buffer[i+6][1]);
			input_im2[1] = (-0.70710678118655f
				*((filter[1]*(buffer[i+1][1]+buffer[i+11][1]))
				+(filter[3]*(buffer[i+3][1]+buffer[i+9][1]))
				-(filter[5]*(buffer[i+5][1]+buffer[i+7][1]))));

			/* q == 0 */
			X_hybrid[i][0][0] = input_re1[0]+input_re1[1]+input_im1[0]+input_im1[1];
			X_hybrid[i][0][1] = -input_re2[0]-input_re2[1]+input_im2[0]+input_im2[1];

			/* q == 1 */
			X_hybrid[i][1][0] = input_re1[0]-input_re1[1]-input_im1[0]+input_im1[1];
			X_hybrid[i][1][1] = input_re2[0]-input_re2[1]+input_im2[0]-input_im2[1];

			/* q == 2 */
			X_hybrid[i][2][0] = input_re1[0]-input_re1[1]+input_im1[0]-input_im1[1];
			X_hybrid[i][2][1] = -input_re2[0]+input_re2[1]+input_im2[0]-input_im2[1];

			/* q == 3 */
			X_hybrid[i][3][0] = input_re1[0]+input_re1[1]-input_im1[0]-input_im1[1];
			X_hybrid[i][3][1] = input_re2[0]+input_re2[1]+input_im2[0]+input_im2[1];
		}
	}

	static void DCT3_4_unscaled(float[] y, float[] x) {
		float f0, f1, f2, f3, f4, f5, f6, f7, f8;

		f0 = (x[2]*0.7071067811865476f);
		f1 = x[0]-f0;
		f2 = x[0]+f0;
		f3 = x[1]+x[3];
		f4 = (x[1]*1.3065629648763766f);
		f5 = (f3*(-0.9238795325112866f));
		f6 = (x[3]*(-0.5411961001461967f));
		f7 = f4+f5;
		f8 = f6-f5;
		y[3] = f2-f8;
		y[0] = f2+f8;
		y[2] = f1-f7;
		y[1] = f1+f7;
	}

	/* complex filter, size 8 */
	void channel_filter8(int frame_len, float[] filter,
		float[][] buffer, float[][][] X_hybrid) {
		int i, n;
		float[] input_re1 = new float[4], input_re2 = new float[4];
		float[] input_im1 = new float[4], input_im2 = new float[4];
		float[] x = new float[4];

		for(i = 0; i<frame_len; i++) {
			input_re1[0] = (filter[6]*buffer[6+i][0]);
			input_re1[1] = (filter[5]*(buffer[5+i][0]+buffer[7+i][0]));
			input_re1[2] = -(filter[0]*(buffer[0+i][0]+buffer[12+i][0]))+(filter[4]*(buffer[4+i][0]+buffer[8+i][0]));
			input_re1[3] = -(filter[1]*(buffer[1+i][0]+buffer[11+i][0]))+(filter[3]*(buffer[3+i][0]+buffer[9+i][0]));

			input_im1[0] = (filter[5]*(buffer[7+i][1]-buffer[5+i][1]));
			input_im1[1] = (filter[0]*(buffer[12+i][1]-buffer[0+i][1]))+(filter[4]*(buffer[8+i][1]-buffer[4+i][1]));
			input_im1[2] = (filter[1]*(buffer[11+i][1]-buffer[1+i][1]))+(filter[3]*(buffer[9+i][1]-buffer[3+i][1]));
			input_im1[3] = (filter[2]*(buffer[10+i][1]-buffer[2+i][1]));

			for(n = 0; n<4; n++) {
				x[n] = input_re1[n]-input_im1[3-n];
			}
			DCT3_4_unscaled(x, x);
			X_hybrid[i][7][0] = x[0];
			X_hybrid[i][5][0] = x[2];
			X_hybrid[i][3][0] = x[3];
			X_hybrid[i][1][0] = x[1];

			for(n = 0; n<4; n++) {
				x[n] = input_re1[n]+input_im1[3-n];
			}
			DCT3_4_unscaled(x, x);
			X_hybrid[i][6][0] = x[1];
			X_hybrid[i][4][0] = x[3];
			X_hybrid[i][2][0] = x[2];
			X_hybrid[i][0][0] = x[0];

			input_im2[0] = (filter[6]*buffer[6+i][1]);
			input_im2[1] = (filter[5]*(buffer[5+i][1]+buffer[7+i][1]));
			input_im2[2] = -(filter[0]*(buffer[0+i][1]+buffer[12+i][1]))+(filter[4]*(buffer[4+i][1]+buffer[8+i][1]));
			input_im2[3] = -(filter[1]*(buffer[1+i][1]+buffer[11+i][1]))+(filter[3]*(buffer[3+i][1]+buffer[9+i][1]));

			input_re2[0] = (filter[5]*(buffer[7+i][0]-buffer[5+i][0]));
			input_re2[1] = (filter[0]*(buffer[12+i][0]-buffer[0+i][0]))+(filter[4]*(buffer[8+i][0]-buffer[4+i][0]));
			input_re2[2] = (filter[1]*(buffer[11+i][0]-buffer[1+i][0]))+(filter[3]*(buffer[9+i][0]-buffer[3+i][0]));
			input_re2[3] = (filter[2]*(buffer[10+i][0]-buffer[2+i][0]));

			for(n = 0; n<4; n++) {
				x[n] = input_im2[n]+input_re2[3-n];
			}
			DCT3_4_unscaled(x, x);
			X_hybrid[i][7][1] = x[0];
			X_hybrid[i][5][1] = x[2];
			X_hybrid[i][3][1] = x[3];
			X_hybrid[i][1][1] = x[1];

			for(n = 0; n<4; n++) {
				x[n] = input_im2[n]-input_re2[3-n];
			}
			DCT3_4_unscaled(x, x);
			X_hybrid[i][6][1] = x[1];
			X_hybrid[i][4][1] = x[3];
			X_hybrid[i][2][1] = x[2];
			X_hybrid[i][0][1] = x[0];
		}
	}

	void DCT3_6_unscaled(float[] y, float[] x) {
		float f0, f1, f2, f3, f4, f5, f6, f7;

		f0 = (x[3]*0.70710678118655f);
		f1 = x[0]+f0;
		f2 = x[0]-f0;
		f3 = ((x[1]-x[5])*0.70710678118655f);
		f4 = (x[2]*0.86602540378444f)+(x[4]*0.5f);
		f5 = f4-x[4];
		f6 = (x[1]*0.96592582628907f)+(x[5]*0.25881904510252f);
		f7 = f6-f3;
		y[0] = f1+f6+f4;
		y[1] = f2+f3-x[4];
		y[2] = f7+f2-f5;
		y[3] = f1-f7-f5;
		y[4] = f1-f3-x[4];
		y[5] = f2-f6+f4;
	}

	/* complex filter, size 12 */
	void channel_filter12(int frame_len, float[] filter,
		float[][] buffer, float[][][] X_hybrid) {
		int i, n;
		float[] input_re1 = new float[6], input_re2 = new float[6];
		float[] input_im1 = new float[6], input_im2 = new float[6];
		float[] out_re1 = new float[6], out_re2 = new float[6];
		float[] out_im1 = new float[6], out_im2 = new float[6];

		for(i = 0; i<frame_len; i++) {
			for(n = 0; n<6; n++) {
				if(n==0) {
					input_re1[0] = (buffer[6+i][0]*filter[6]);
					input_re2[0] = (buffer[6+i][1]*filter[6]);
				}
				else {
					input_re1[6-n] = ((buffer[n+i][0]+buffer[12-n+i][0])*filter[n]);
					input_re2[6-n] = ((buffer[n+i][1]+buffer[12-n+i][1])*filter[n]);
				}
				input_im2[n] = ((buffer[n+i][0]-buffer[12-n+i][0])*filter[n]);
				input_im1[n] = ((buffer[n+i][1]-buffer[12-n+i][1])*filter[n]);
			}

			DCT3_6_unscaled(out_re1, input_re1);
			DCT3_6_unscaled(out_re2, input_re2);

			DCT3_6_unscaled(out_im1, input_im1);
			DCT3_6_unscaled(out_im2, input_im2);

			for(n = 0; n<6; n += 2) {
				X_hybrid[i][n][0] = out_re1[n]-out_im1[n];
				X_hybrid[i][n][1] = out_re2[n]+out_im2[n];
				X_hybrid[i][n+1][0] = out_re1[n+1]+out_im1[n+1];
				X_hybrid[i][n+1][1] = out_re2[n+1]-out_im2[n+1];

				X_hybrid[i][10-n][0] = out_re1[n+1]-out_im1[n+1];
				X_hybrid[i][10-n][1] = out_re2[n+1]+out_im2[n+1];
				X_hybrid[i][11-n][0] = out_re1[n]+out_im1[n];
				X_hybrid[i][11-n][1] = out_re2[n]-out_im2[n];
			}
		}
	}

	void hybrid_synthesis(float[][][] X, float[][][] X_hybrid,
		boolean use34, int numTimeSlotsRate) {
		int k, n, band;
		int offset = 0;
		int qmf_bands = (use34) ? 5 : 3;
		int[] resolution = (use34) ? this.resolution34 : this.resolution20;

		for(band = 0; band<qmf_bands; band++) {
			for(n = 0; n<this.frame_len; n++) {
				X[n][band][0] = 0;
				X[n][band][1] = 0;

				for(k = 0; k<resolution[band]; k++) {
					X[n][band][0] += X_hybrid[n][offset+k][0];
					X[n][band][1] += X_hybrid[n][offset+k][1];
				}
			}
			offset += resolution[band];
		}
	}
}