[英]Efficiently convert audio bytes - byte[] to short[]
我正在尝试使用XNA麦克风捕获音频并将其传递给我分析数据用于显示目的的API。 但是,API要求音频数据为16位整数数组。 所以我的问题很简单。 将字节数组转换为短数组的最有效方法是什么?
private void _microphone_BufferReady(object sender, System.EventArgs e)
{
_microphone.GetData(_buffer);
short[] shorts;
//Convert and pass the 16 bit samples
ProcessData(shorts);
}
干杯,戴夫
编辑 :这是我想出的,似乎可以使用,但是可以更快地完成吗?
private short[] ConvertBytesToShorts(byte[] bytesBuffer)
{
//Shorts array should be half the size of the bytes buffer, as each short represents 2 bytes (16bits)
short[] shorts = new short[bytesBuffer.Length / 2];
int currentStartIndex = 0;
for (int i = 0; i < shorts.Length - 1; i++)
{
//Convert the 2 bytes at the currentStartIndex to a short
shorts[i] = BitConverter.ToInt16(bytesBuffer, currentStartIndex);
//increment by 2, ready to combine the next 2 bytes in the buffer
currentStartIndex += 2;
}
return shorts;
}
阅读您的更新后,我可以看到您实际上需要将字节数组直接直接复制到短裤缓冲区(合并字节)中。 这是文档中的相关部分:
用作SoundEffect构造函数,Microphone.GetData方法和DynamicSoundEffectInstance.SubmitBuffer方法的参数的byte []缓冲区格式是PCM波形数据。 此外,PCM格式是交织的,并且采用低位字节序。
现在,如果出于某种奇怪的原因,您的系统具有BitConverter.IsLittleEndian == false
,那么您将需要遍历缓冲区,并随即交换字节,以从little-endian转换为big-endian。 我将把代码留作练习-我可以肯定地说,所有XNA系统都是低位优先的。
为了您的目的,您可以直接使用Marshal.Copy
或Buffer.BlockCopy
复制缓冲区。 两者都将为您提供平台本机内存复制操作的性能,这将非常快:
// Create this buffer once and reuse it! Don't recreate it each time!
short[] shorts = new short[_buffer.Length/2];
// Option one:
unsafe
{
fixed(short* pShorts = shorts)
Marshal.Copy(_buffer, 0, (IntPtr)pShorts, _buffer.Length);
}
// Option two:
Buffer.BlockCopy(_buffer, 0, shorts, 0, _buffer.Length);
这是一个性能问题,因此: 测量一下!
值得指出的是,为了评估.NET的性能,您需要在不附加调试器的情况下进行发行版本构建和运行(这可以使JIT得以优化)。
Jodrell的答案值得一提:使用AsParallel
很有趣,但是值得检查将其AsParallel
的成本是否值得。 (推测-对其进行测量以确认:将字节转换为short应该非常快,因此,如果缓冲区数据来自共享内存而不是每个内核缓存,则您的大部分成本可能会用于数据传输而不是处理。)
另外,我不确定ToArray
是否合适。 首先,它可能无法直接创建正确大小的数组,必须在构建数组时调整其大小,这会使它非常慢。 此外,它将始终分配数组-数组本身并不慢,但会增加您几乎肯定不希望的GC成本。
因此,您想预分配阵列。 在代码中的某个地方,您需要这样的缓冲区:
short[] shorts = new short[_buffer.Length];
然后简单地从一个缓冲区复制到另一个缓冲区:
for(int i = 0; i < _buffer.Length; ++i)
result[i] = ((short)buffer[i]);
这应该非常快,并且如果不是两个数组边界检查都可以,JIT应该足够聪明以跳过一个。
这是使用不安全代码的方法:(我尚未测试此代码,但应该正确)
unsafe
{
int length = _buffer.Length;
fixed(byte* pSrc = _buffer) fixed(short* pDst = shorts)
{
byte* ps = pSrc;
short* pd = pDst;
while(pd < pd + length)
*(pd++) = (short)(*(ps++));
}
}
现在,不安全的版本具有需要/unsafe
的缺点,并且实际上可能会更慢,因为它会阻止JIT进行各种优化。 再次: 测量它 。
(另外,如果您尝试对上述示例进行一些置换,则可能会降低性能。请对其进行测量 。)
最后:确定要转换为(short)sample
吗? 像((short)sample-128)*256
将它从无符号带到带符号并将其扩展到正确的位宽应该不是吗?
我可以想到的害虫PLINQ在这里。
private short[] ConvertBytesToShorts(byte[] bytesBuffer)
{
//Shorts array should be half the size of the bytes buffer, as each short represents 2 bytes (16bits)
var odd = buffer.AsParallel().Where((b, i) => i % 2 != 0);
var even = buffer.AsParallell().Where((b, i) => i % 2 == 0);
return odd.Zip(even, (o, e) => {
return (short)((o << 8) | e);
}.ToArray();
}
我对性能很感兴趣,但有足够的数据和处理器知道。
如果转换操作错误( (short)((o << 8) | e)
),请更改为适合。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.