I'm trying to create a 16-bit PCM version of NAudio's MixingWaveProvider32 that operates on 16-bit PCM samples instead of 32-bit floats.
Each 16-bit stereo sample is packed in a byte array like so...
Byte 0 | Byte 1 | Byte 2 | Byte 3 |
---|---|---|---|
Channel 1 (Left) Lo | Channel 1 Hi | Channel 2 (Right) Lo | Channel 2 Hi |
The two bytes per channel are interpreted as signed integers, so the minimum value is short.MinValue, the max is short.MaxValue. I don't think you can simply add the byte values to each other.
I've written some very long-handed code (see below) but I am convinced there is a more performant way of doing this.
I'd be really grateful for any help :-)
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();
}
You could always use unsafe code, this should be significantly faster since you save a bunch of method calls and object allocations:
// 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]}");
}
}
}
}
Note that summing values might cause overflow. You should probably either average the two samples, or clamp the result to avoid this.