簡體   English   中英

有效地將音頻字節-byte []轉換為short []

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM