You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity C#音频float[]数据压缩求助:多人游戏语音聊天优化

语音压缩优化方案

一、修复基础float转byte的错误(无损压缩,体积降至原float数组的25%)

你之前的转换逻辑忽略了音频float采样值的**-1.0f到1.0f**范围,负数被直接截断为0,这是噪音大的核心原因。以下是修正后的代码:

public static byte[] ConvertAudioClipToByteArray(float[] data)
{
    byte[] bytes = new byte[data.Length];
    for (int i = 0; i < data.Length; i++)
    {
        // 将-1~1的float映射到0~255的byte区间
        float normalized = (data[i] + 1f) / 2f;
        bytes[i] = (byte)(normalized * 255f);
    }
    return bytes;
}

public static float[] ConvertByteArrayToAudioClip(byte[] bytes)
{
    float[] samples = new float[bytes.Length];
    for (int i = 0; i < bytes.Length; i++)
    {
        // 从0~255映射回-1~1的float区间
        float normalized = bytes[i] / 255f;
        samples[i] = (normalized * 2f) - 1f;
    }
    return samples;
}

该方案将每个4字节的float转为1字节的byte,体积直接压缩75%,且音质损失极小,完全满足实时语音需求。

二、语音专用有损压缩(进一步减小体积)

如果上述方案的体积仍不符合要求,可采用针对语音优化的压缩算法,以下是两种游戏场景常用方案:

1. ADPCM压缩(体积降至原float数组的12.5%)

ADPCM利用语音采样的连续性,仅存储当前采样与上一采样的差值,每个采样用4bit存储,体积是上述byte方案的一半:

// 简化版ADPCM压缩(4bit/采样)
public static byte[] CompressADPCM(float[] data)
{
    // 每2个采样打包为1个byte
    byte[] compressed = new byte[(data.Length + 1) / 2];
    float lastSample = 0f;
    int byteIndex = 0;

    for (int i = 0; i < data.Length; i += 2)
    {
        // 计算差值并映射到-7~7(4bit有符号范围)
        int delta1 = (int)Math.Clamp((data[i] - lastSample) * 127f, -7, 7);
        lastSample = data[i] + delta1 / 127f;

        int delta2 = 0;
        if (i + 1 < data.Length)
        {
            delta2 = (int)Math.Clamp((data[i+1] - lastSample) * 127f, -7, 7);
            lastSample = data[i+1] + delta2 / 127f;
        }

        // 两个4bit值打包为1个byte
        compressed[byteIndex] = (byte)((delta1 << 4) | (delta2 & 0x0F));
        byteIndex++;
    }
    return compressed;
}

public static float[] DecompressADPCM(byte[] compressed, int originalLength)
{
    float[] data = new float[originalLength];
    float lastSample = 0f;
    int sampleIndex = 0;

    foreach (byte b in compressed)
    {
        // 解析第一个4bit差值
        int delta1 = (b >> 4) & 0x0F;
        delta1 = delta1 > 7 ? delta1 - 16 : delta1;
        data[sampleIndex] = lastSample + delta1 / 127f;
        lastSample = data[sampleIndex];
        sampleIndex++;

        if (sampleIndex >= originalLength) break;

        // 解析第二个4bit差值
        int delta2 = b & 0x0F;
        delta2 = delta2 > 7 ? delta2 - 16 : delta2;
        data[sampleIndex] = lastSample + delta2 / 127f;
        lastSample = data[sampleIndex];
        sampleIndex++;
    }
    return data;
}

该方案压缩率极高,且语音清晰度足够支撑游戏近距离聊天场景。

2. 静默检测压缩(降低平均带宽)

语音聊天大部分时间为静默或低音量,可通过音量检测跳过或极简压缩静默段:

public static byte[] CompressWithSilenceDetection(float[] data, float silenceThreshold = 0.01f)
{
    List<byte> compressed = new List<byte>();
    bool inSilence = false;
    int silenceStart = 0;

    for (int i = 0; i < data.Length; i++)
    {
        bool isSilence = Math.Abs(data[i]) < silenceThreshold;
        if (isSilence && !inSilence)
        {
            inSilence = true;
            silenceStart = i;
        }
        else if (!isSilence && inSilence)
        {
            // 用2字节存储静默长度
            compressed.AddRange(BitConverter.GetBytes((ushort)(i - silenceStart)));
            // 标记非静默段开始
            compressed.Add(0xFF);
            inSilence = false;
        }

        if (!inSilence)
        {
            float normalized = (data[i] + 1f) / 2f;
            compressed.Add((byte)(normalized * 255f));
        }
    }

    // 处理结尾的静默段
    if (inSilence)
    {
        compressed.AddRange(BitConverter.GetBytes((ushort)(data.Length - silenceStart)));
    }

    return compressed.ToArray();
}

public static float[] DecompressWithSilenceDetection(byte[] compressed)
{
    List<float> data = new List<float>();
    int i = 0;

    while (i < compressed.Length)
    {
        if (compressed[i] == 0xFF)
        {
            // 解析非静默段
            i++;
            while (i < compressed.Length && compressed[i] != 0xFF)
            {
                float normalized = compressed[i] / 255f;
                data.Add((normalized * 2f) - 1f);
                i++;
            }
        }
        else
        {
            // 解析静默段,填充0值
            ushort silenceLength = BitConverter.ToUInt16(compressed, i);
            i += 2;
            for (int j = 0; j < silenceLength; j++)
            {
                data.Add(0f);
            }
        }
    }

    return data.ToArray();
}

该方案在用户未说话时几乎不产生数据,能大幅降低平均带宽占用。

三、丢包补偿补充

针对丢包问题,可在接收端加入简单补偿逻辑:若检测到丢包,用前3-5个采样的平均值填充缺失段,避免出现刺耳噪音。


内容的提问来源于stack exchange,提问作者Erfan_Games

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:55:09