2011-04-20 69 views
2

我正在嘗試使用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; 

    } 
+0

是否需要將「bytes」組合爲「shorts」,還是更像是迭代演員? – Jodrell 2011-04-20 11:13:52

+0

什麼類型是_buffer,byte []? – Jodrell 2011-04-20 12:13:02

+0

@Jodrell - 是的,它是.. – 2011-04-20 12:39:41

回答

5

在閱讀您的更新後,我可以看到您需要將一個字節數組直接複製到短褲的合併字節中。下面是來自documentation的相關部分:

用作SoundEffect中構造,Microphone.GetData方法中的參數的字節[]緩衝格式,並且DynamicSoundEffectInstance.SubmitBuffer方法是PCM波形數據。另外,PCM格式是交織的並且是小端的。現在

,如果出於某種奇怪的原因你的系統有BitConverter.IsLittleEndian == false,則需要遍歷您的緩衝區,交換字節,當您去,從小端轉換爲大端。我將把代碼留作練習 - 我確信所有的XNA系統都是小端的。

出於您的目的,您可以直接使用Marshal.CopyBuffer.BlockCopy直接複製緩衝區。兩者都會爲您提供平臺本機內存複製操作的性能,這將非常快速:

​​
+0

+1可能更快? – Jodrell 2011-04-21 07:55:00

1

這是一個性能問題,所以:衡量它!

這是值得指出的是,在.NET衡量性能,你想要做一個發佈版本,並沒有運行連接調試器(這使得JIT優化)。

Jodrell的回答值得評論:使用AsParallel很有意思,但值得一提的是檢查它是否值得。 (推測 - 測量它以確認:將字節轉換爲短應該是非常快的,所以如果您的緩衝區數據來自共享內存而不是每個核心緩存,則大部分成本可能會在數據傳輸不處理中。)

另外我不確定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這樣從無符號到帶符號並將其擴展到正確的位寬嗎? 更新:似乎我錯了這裏的格式,看到我的其他答案

0

我可以想到的害蟲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(); 
} 

我是性能的dubios,但有足夠的數據和處理器誰知道。

如果轉換操作錯誤((short)((o << 8) | e))請更改爲適合。