混合 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]}");
}
}
}
}
请注意,求和值可能会导致溢出。您可能应该对两个样本进行平均,或者限制结果以避免这种情况。
我正在尝试创建 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]}");
}
}
}
}
请注意,求和值可能会导致溢出。您可能应该对两个样本进行平均,或者限制结果以避免这种情况。