您的位置:

G.711音频编解码算法

一、概述

G.711是国际电信联盟(ITU)为语音通信和录音等应用而制定的音频编解码标准。该标准主要用于传输纯语音信息,包括电话、对讲机等,不支持音频信号的压缩。G.711采用时域抽样,将模拟音频采样率从8kHz上升至16kHz,保证传输音频质量。

二、G.711编解码原理

G.711有两种编解码方式,分别为u-law(μ-law)和A-law。这两种编码方式是根据编码器的压缩特性进行选择的。

1. u-law编码

short linear2ulaw(int pcm_val)
{
    int     mask;
    int     seg;
    unsigned char   uval;

    pcm_val = pcm_val >> 2;

    if (pcm_val < 0) {
        pcm_val = -pcm_val;
        mask = 0x7F;
    } else {
        mask = 0xFF;
    }

    if (pcm_val > 0x3FFF) {
        pcm_val = 0x3FFF;
    }

    pcm_val += 0x20;
    seg = compand[pcm_val>>6];
    uval = (unsigned char)(seg | ((pcm_val >>(seg + 1)) & 0x0F));
    return (short)(uval ^ mask);
}

2. A-law编码

short linear2alaw(int pcm_val)
{
    int     mask;
    int     seg;
    unsigned char   aval;

    if (pcm_val >= 0) {
        mask = 0xD5;
    } else {
        mask = 0x55;
        pcm_val = -pcm_val - 8;
    }

    if (pcm_val < 0) {
        pcm_val = 0;
    }

    if (pcm_val > 0x7FF) {
        pcm_val = 0x7FF;
    }

    pcm_val >>= 4;
    seg = search(pcm_val, seg_end, 8);
    if (seg >= 8) {
        return (short)(0x7F ^ mask);
    } else {
        aval = (unsigned char)(seg << 4 | (pcm_val >> (seg + 1) & 0x0F));
        return (short)(aval ^ mask);
    }
}

三、G.711编码器的实现

以下代码展示了一个简单的G.711编码器实现:

public class G711Encoder {
    static final int ZEROTRAP = 1;
    static final int BIAS = 0x84;
    static final int CLIP = 8159;

    private static byte[] _law2linear;

    /**
     * 静态块,初始化_law2linear表
     */
    static {
        _law2linear = new byte[1024];
        for (int i = 0; i < 256; i++) {
            int x = i ^ 0x55;
            x <<= 7;
            short t = (short) x;
            if ((x & 0x8000) != 0) {
                t = (short) (0x7fff - (x & 0x7fff));
            }

            t -= BIAS << 7;
            _law2linear[i] = (byte) (t >>> 8);
            _law2linear[i + 256] = (byte) (t & 0xff);
            x = (i & 0x7f) << 8;
            t = (short) x;
            if ((x & 0x8000) != 0) {
                t = (short) (0x7fff - (x & 0x7fff));
            }

            t -= (BIAS << 3) - 1;
            _law2linear[i + 512] = (byte) (t >>> 8);
            _law2linear[i + 768] = (byte) (t & 0xff);
        }
    }

    /**
     * 编码PCM到G.711
     *
     * @param pcm 输入PCM数据
     * @return 返回G.711编码后的数据
     */
    public byte[] encode(byte[] pcm) {
        byte[] g711 = new byte[pcm.length / 2];
        int idx, x;
        for (int i = 0, s = 0; i < pcm.length; i += 2) {
            idx = ((pcm[i + 1] & 0xff) << 8) | (pcm[i] & 0xff);
            if (idx < 0) {
                idx = 0;
            } else if (idx > 32767) {
                idx = 32767;
            }

            x = _law2linear[idx];
            if (pcm[i + 1] < 0) {
                x = -x;
            }

            x += 0x80;
            if (x < 0) {
                x = 0;
            } else if (x > 0xFF) {
                x = 0xFF;
            }

            g711[s++] = (byte) x;
        }
        return g711;
    }

}

四、G.711解码器的实现

以下代码展示了一个简单的G.711解码器实现:

public class G711Decoder {
    static final int ZEROTRAP = 1;
    static final int BIAS = 0x84;
    static final int CLIP = 8159;

    private static short[] _linear2law;

    /**
     * 静态块,初始化_linear2law表
     */
    static {
        int[] seg_end = {0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF};
        _linear2law = new short[65536];
        for (int i = 0; i < 65536; i++) {
            short sign = 0;
            if (i < 0) {
                i = -i;
                sign = 0x80;
            }
            int exponent = search(i, seg_end, 8) << 3;
            int mantissa = (i >> (exponent + 3)) & 0x0F;
            _linear2law[i] = (short) (sign | exponent | mantissa);
        }
    }

    /**
     * 解码G.711到PCM
     *
     * @param g711 输入G.711数据
     * @return 返回PCM解码后的数据
     */
    public byte[] decode(byte[] g711) {
        byte[] pcm = new byte[g711.length * 2];
        int idx, sign, x;
        for (int i = 0, s = 0; i < g711.length; i++, s += 2) {
            sign = (g711[i] & 0x80) != 0 ? (byte) 0x00FF : 0;
            idx = ((g711[i] ^ sign) & 0xff);
            x = _linear2law[idx] + BIAS << 7;
            if (sign != 0) {
                x = -x;
            }

            pcm[s] = (byte) (x & 0xff);
            pcm[s + 1] = (byte) (x >> 8);
        }
        return pcm;
    }
}

五、应用举例

G.711主要在电话语音通信、对讲机等领域中得到广泛使用。以下是一个Java语言中使用G.711编解码的例子:

public class G711Example {
    public static void main(String[] args) {
        short[] pcmData = new short[]{0, 100, 200, 300, 400, 500};
        G711Encoder encoder = new G711Encoder();
        G711Decoder decoder = new G711Decoder();
        byte[] g711Data = encoder.encode(ArrayUtils.toBytes(pcmData)); // PCM -> G.711
        short[] pcmData2 = ArrayUtils.toShorts(decoder.decode(g711Data)); // G.711 -> PCM
        System.out.println(Arrays.toString(pcmData));
        System.out.println(Bytes.toHexString(g711Data, "", " ", ""));
        System.out.println(Arrays.toString(pcmData2));
    }
}