package org.wlld.voice;


import java.io.IOException;
import java.io.InputStream;

public class MP3 {

    /**
     * 横坐标为MPEG(V),纵坐标为Layer(L),sample[0][2]为MPEG-1,Layer-3的每帧采样数
     */

    private final static int[][] Mp3_Sample = {{384, 384, 384},
            {1152, 1152, 1152}, {1152, 576, 576}};

    /**
     * 二维数组长度为14,横坐标范围:1~14.MPEG1中,纵坐标分别对应Layer-1,Layer-2,Layer-3.MPEG2中,纵坐标分别对应Layer-1,Layer-2或Layer-3.
     */
    private final static int[][] MPeg1ByteRate = {{32, 32, 32},
            {64, 48, 40}, {96, 56, 48}, {128, 64, 56}, {160, 80, 64},
            {192, 96, 80}, {224, 112, 96}, {256, 128, 112},
            {288, 160, 128}, {320, 192, 160}, {352, 224, 192},
            {384, 256, 224}, {416, 320, 256}, {448, 384, 320}};

    private final static int[][] MPeg2ByteRate = {{32, 8}, {48, 16},
            {56, 24}, {64, 32}, {80, 40}, {96, 48}, {112, 56},
            {128, 64}, {144, 80}, {160, 96}, {176, 112}, {192, 128},
            {224, 144}, {256, 160}};

    /**
     * 采样频率.横坐标为变量,纵坐标为MPEG版本.
     */
    private final static int[][] SampleFrequency = {{44100, 48000, 32000},
            {22050, 24000, 16000}, {11025, 12000, 8000}};

    // ==================================================================================
    private InputStream stream;

    public MP3(InputStream stream) {
        this.stream = stream;
    }

    /**
     * MpeG版本
     */
    private int Mpeg_Version;

    /**
     * Layer版本
     */
    private int Layer_Version;

    /**
     * 采样速率.(kbps)
     */
    private int ByteRate;

    /**
     * 采样频率.khz
     */
    private int Frequency;

    /**
     * 帧长度调整值.0或1
     */
    private int Padding;

    /**
     * 采样位数.
     */
    private int Sample;

    public int parserMp3Header() throws IOException {
        FrameHeader = new int[3];
        byte[] tempBytes = readFull(3);
        if (tempBytes == null) {
            return -1;
        }
        for (int i = 0; i < 3; i++) {
            FrameHeader[i] = tempBytes[i] & 0xFF;
        }
        int TagHeaderSize = 0;
        if (FrameHeader[0] == 'I' && FrameHeader[1] == 'D'
                && FrameHeader[2] == '3') {
            byte[] tagHeader = readFull(7);
            if (tagHeader == null) {
                return -1;
            }
            int tagHeaderSize = ((tagHeader[3] & 0x7F) << 21)
                    + ((tagHeader[4] & 0x7F) << 14)
                    + ((tagHeader[5] & 0x7F) << 7) + (tagHeader[6] & 0x7F);
            // tagHeaderSize不包括前面10个字节.加上10以后,是整个标签头的大小.
            if (!skipBytes(tagHeaderSize)) {
                return -1;
            }
            // IDV3标签头的长度.
            TagHeaderSize = tagHeaderSize + 10;
            tempBytes = readFull(3);
            if (tempBytes == null) {
                return -1;
            }
            for (int i = 0; i < 3; i++) {
                FrameHeader[i] = tempBytes[i] & 0xFF;
            }
        }
        if (FrameHeader[0] != 0xFF || (FrameHeader[1] >> 5) != 7) {
            return -1;
        }
        switch ((FrameHeader[1] & 24) >> 3) {
            case 0:
                Mpeg_Version = 3;// MPEG-2.5
                break;
            case 2:
                Mpeg_Version = 2;
                break;
            case 3:
                Mpeg_Version = 1;
                break;
            default:
                return -1;
        }
        switch ((FrameHeader[1] & 6) >> 1) {
            case 1:
                Layer_Version = 3;
                break;
            case 2:
                Layer_Version = 2;
                break;
            case 3:
                Layer_Version = 1;
                break;
            default:
                return -1;
        }
        int index = FrameHeader[2] >> 4;
        if (index < 1 || index > 14) {
            return -1;
        }
        --index;
        if (Mpeg_Version == 1) {
            switch (Layer_Version) {
                case 1:
                    ByteRate = MPeg1ByteRate[index][0];
                    break;
                case 2:
                    ByteRate = MPeg1ByteRate[index][1];
                    break;
                case 3:
                    ByteRate = MPeg1ByteRate[index][2];
                    break;
            }
        } else {
            switch (Layer_Version) {
                case 1:
                    ByteRate = MPeg2ByteRate[index][0];
                    break;
                case 2:
                case 3:
                    ByteRate = MPeg2ByteRate[index][1];
                    break;
            }
        }
        Frequency = SampleFrequency[Mpeg_Version - 1][(FrameHeader[2] & 12) >> 2];
        Padding = (FrameHeader[2] & 2) >> 1;
        Sample = Mp3_Sample[Layer_Version - 1][Mpeg_Version - 1];
        return TagHeaderSize;
    }

    public long SkipFrame(long num) throws IOException {
        long skipped = 0;
        byte[] temp = null;
        for (int i = 0; i < num; i++) {
            temp = ParserFrame();
            if (temp == null) {
                return skipped;
            }
            skipped += temp.length;
        }
        return skipped;
    }

    /**
     * 第一帧的前三个字节.用来提取除采样速率以外的其它定值. 在前三个字节中,第一二两个字节是不变的.只有第三个字节会左右帧长度的变化.
     */
    private int[] FrameHeader;

    /**
     * 发生网络数据错误,或者没有读到需要的数据,都会抛出异常.因此,外部调用程序要控制数据大小,到了MP3.length -
     * 128的时候就不要再读了,程序不会检查是否已经到了末尾.
     *
     * @return byte[] 读到的一帧完整的可以播放的数据.遇到IDV1时,返回NULL
     * @throws IOException
     * @throws Exception
     */
    public byte[] ParserFrame() throws IOException {
        byte[] byteFrameHeader;
        int index = 0;
        if (FrameHeader == null) {
            byteFrameHeader = readFull(3);
            if (byteFrameHeader == null || byteFrameHeader.length < 3
                    || (byteFrameHeader[0] & 0xFF) == 'T'
                    && (byteFrameHeader[1] & 0xFF) == 'A'
                    && (byteFrameHeader[2] & 0xFF) == 'G') {
                return null;
            }
            if ((byteFrameHeader[0] & 0xFF) != 0xFF
                    || ((byteFrameHeader[1] & 0xFF) >> 5) != 7) {
                System.out.println("该MP3文件非法.");
                return null;// 帧头的第一个字节和第二个字节的前三位不全部为1,该MP3文件非法.
            }
            index = (byteFrameHeader[2] & 0xFF) >> 4;
            Padding = (byteFrameHeader[2] & 2) >> 1;
        } else {
            if (FrameHeader == null || FrameHeader.length < 3
                    || FrameHeader[0] == 'T' && FrameHeader[1] == 'A'
                    && FrameHeader[2] == 'G') {
                return null;
            }
            index = FrameHeader[2] >> 4;
            byteFrameHeader = new byte[3];
            byteFrameHeader[0] = (byte) FrameHeader[0];
            byteFrameHeader[0] = (byte) FrameHeader[1];
            byteFrameHeader[0] = (byte) FrameHeader[2];
            FrameHeader = null;
        }
        if (index < 1 || index > 14) {
            return null;// 获取位速的查找索引非法.
        }
        --index;
        if (Mpeg_Version == 1) {
            switch (Layer_Version) {
                case 1:
                    ByteRate = MPeg1ByteRate[index][0];
                    break;
                case 2:
                    ByteRate = MPeg1ByteRate[index][1];
                    break;
                case 3:
                    ByteRate = MPeg1ByteRate[index][2];
                    break;
            }
        } else {
            switch (Layer_Version) {
                case 1:
                    ByteRate = MPeg2ByteRate[index][0];
                    break;
                case 2:
                case 3:
                    ByteRate = MPeg2ByteRate[index][1];
                    break;
            }
        }
        // 计算帧长.(ByteRate,Frequency,Padding是可变的)
        int frameSize = Sample / 8 * ByteRate * 1000 / Frequency + Padding;
        byte[] temp = readFull(frameSize - 3);
        if (temp == null) {
            return null;
        }
        byte[] data = new byte[frameSize];
        System.arraycopy(byteFrameHeader, 0, data, 0, 3);
        System.arraycopy(temp, 0, data, 3, temp.length);
        return data;
    }

    public byte[] readFull(int size) throws IOException {
        byte[] data = new byte[size];
        int n = 0;
        while (n < size) {
            int k = stream.read(data, n, size - n);
            if (k < 0) {
                break;
            }
            n += k;
        }
        if (n <= 0) {
            return null;
        }
        if (n < size) {
            byte[] temp = new byte[n];
            System.arraycopy(data, 0, temp, 0, n);
            data = null;
            return temp;
        }
        return data;
    }

    public boolean skipBytes(long skipLength) throws IOException {
        long k = 0;
        while (k < skipLength) {
            long n = stream.skip(skipLength - k);
            if (n < 0) {
                return false;
            }
            k += n;
        }
        return true;
    }

}
