[英]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.