混合 16 位立体声 PCM 音频

Mixing 16-bit stereo PCM audio

我正在尝试创建 NAudio MixingWaveProvider32 的 16 位 PCM 版本,它在 16 位 PCM 样本而不是 32 位浮点数上运行。

每个 16 位立体声样本都像这样打包在一个字节数组中...

Byte 0 Byte 1 Byte 2 Byte 3
Channel 1 (Left) Lo Channel 1 Hi Channel 2 (Right) Lo Channel 2 Hi

每个通道的两个字节被解释为有符号整数,因此最小值为 short.MinValue,最大值为 short.MaxValue。我认为您不能简单地将字节值相加。

我已经编写了一些非常冗长的代码(见下文),但我相信有一种更高效的方法可以做到这一点。

如果有任何帮助,我将不胜感激:-)

static void Main(string[] args)
{
    // setup some input data
    byte[] b1 = { 0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0 };
    byte[] b2 = new byte[b1.Length];

    Array.Copy(b1, b2, b1.Length);

    byte[] result = new byte[b1.Length];

    Console.WriteLine("b1");
    b1.DumpPcm();

    Console.WriteLine();

    Console.WriteLine("b2");
    b2.DumpPcm();

    for (int i = 0; i < b1.Length; i += 4)
    {
        short l1 = BitConverter.ToInt16(b1, i);
        short r1 = BitConverter.ToInt16(b1, i + 2);

        short l2 = BitConverter.ToInt16(b2, i);
        short r2 = BitConverter.ToInt16(b2, i + 2);

        byte[] resl = BitConverter.GetBytes(l1 + l2);
        byte[] resr = BitConverter.GetBytes(r1 + r2);

        result[i] = resl[0];
        result[i + 1] = resl[1];
        result[i + 2] = resr[0];
        result[i + 3] = resr[1];
    }

    Console.WriteLine();
    Console.WriteLine("Result...");

    result.DumpPcm();

    Console.ReadLine();
}

你总是可以使用不安全的代码,这应该会快得多,因为你节省了一堆方法调用和对象分配:

        // setup some input data
        byte[] b1 = {0x1, 0x0, 0x2, 0x0, 0x3, 0x0, 0x4, 0x0};
        byte[] b2 = new byte[b1.Length];
        Array.Copy(b1, b2, b1.Length);
        byte[] result = new byte[b1.Length];

        fixed (byte* b1Ptr = b1)
        {
            fixed (byte* b2Ptr = b2)
            {
                fixed (byte* rPtr = result)
                {
                    var s1Ptr = (short*) b1Ptr;
                    var s2Ptr = (short*) b2Ptr;
                    var srPtr = (short*) rPtr;
                    var length = b1.Length / 2;
                    for (int i = 0; i < length; i++)
                    {
                        var v = s1Ptr[i] + s2Ptr[i];
                        srPtr[i] = (short) v;
                        Console.WriteLine($"{s1Ptr[i]} + {s2Ptr[i]} -> {srPtr[i]}");
                    }
                }
            }
        }

请注意,求和值可能会导致溢出。您可能应该对两个样本进行平均,或者限制结果以避免这种情况。