2016-08-28 14 views
1

私はサウンドミキサーを実装していますが、SIMD命令を使用しなくてもうまく動作しますが、サウンドデータを別々のチャネルに抽出する方法はわかりません。SIMD命令を使用したオーディオチャネルのデインターリーブ

私のデータはインターリーブ形式になります:L0R0 L1R1 L2R2 L3R3 ... 同じフォーマットで__m128iにロードするので、レジスタに4つのサンプルがあります。

私はそれらを別々のチャンネルにしたいと思います:L0L1L2L3 R0R1R2R3。これは私が紛失している部分です。

入力が8 x i16(4xi32インターリーブ) 出力を左= 4 x f32、右= 4 x f32としたい場合は、混合してください。

に混合した後、私はチャンネルをインターリーブすることができますし、私はL0R0 L1R1 L2R2を取得するには...:

__m128 *src0 = mixed_channel0; 
__m128 *src1 = mixed_channel1; 
__m128 *dest = (__m128i *)buffer; 

for (u32 sample_index = 0; sample_index < sample_chunk_count; ++sample_index) 
{ 
    __m128 s0 = _mm_load_ps((f32 *)src0++); 
    __m128 s1 = _mm_load_ps((f32 *)src1++); 

    __m128i l = _mm_cvtps_epi32(s0); 
    __m128i r = _mm_cvtps_epi32(s1); 

    __m128i lr0 = _mm_unpacklo_epi32(l, r); 
    __m128i lr1 = _mm_unpackhi_epi32(l, r); 

    *dest++ = _mm_packs_epi32(lr0, lr1); 
} 

は基本的に私は反対のことを実行する必要があります。

__m128i input = [L0R0, L1R1, L2R2, L3R3] packed pairs of 16bit ints 
// magic happens, then 
__m128 left = [L0, L1, L2, L3] packed 32bit floats 
__m128 right = [R0, R1, R2, R3] packed 32bit floats 

私はマスクしていても低/高位のi16-s、次に私はそれらをf32-sに変換できますか?マスキングアウト後、私はなるだろう:

__m128i right = [xx, R0, xx, R1, xx, R2, xx, R3] 
__m128i left = [L0, xx, L1, xx, L2, xx, L3, xx] 

私は4×I32-Sに変換することができれば、_mm_cvtepi32_psとF32-Sに変換するのは簡単だろうと私は行われるだろう。

ありがとうございました。

+0

低/高の 'i16'を変換するための最善の策はビットシフトです。 'high_halves = _mm_srai_epi32(packed、16);'。あなたの値が署名されているので、おそらく、左シフトと算術右シフトを使って下半分を符号拡張する必要があります。私はより良い方法のATMを考えることができませんが、これは一種のように見えます。 –

+0

いくつかのSIMD命令セット(ARM NEON/ARMv8など)には、2入力または2出力の命令が2つあります。これを1つの命令で解凍することができます。 (たぶん解凍、IIRC)。だから、SIMDだけでなく、Intel SSEを使って具体的に言うことが重要です。 –

+0

[SSE3](https://blogs.msdn.microsoft.com/chuckw/2012/09/11/directxmath-sse3-and-ssse3/)が必要ですか? SSE/SSE2(https://blogs.msdn.microsoft.com/chuckw/2012/09/11/directxmath-sse-sse2-and-arm-neon/)の方法は「必須」ではありませんが、現代的ですPC、それは非常に一般的です(スティームゲーマーの場合は91%です)。 '' _mm_moveldup_ps''と '' _mm_movehdup_ps''は非常に便利です。 –

答えて

0

16ビットのサンプルのペアから32ビットのサンプルをマスクしてシフトします。

// clunky calling convention, but should inline ok. 
__m128 unpack_leftright_16bit_channels(__m128i input, __m128 &right_retval) { 
    // input = [L0R0, L1R1, L2R2, L3R3] packed pairs of 16bit ints 
    __m128i sign_extended_left = _mm_srai_epi32(input, 16); 
    __m128i high_right = _mm_slli_epi32(input, 16); 
    __m128i sign_extended_right = _mm_srai_epi32(high_right, 16); 

    right_retval = _mm_cvtepi32_ps(sign_extended_right); 
    //__m128 right = [R0, R1, R2, R3] packed 32bit floats 

    __m128 left = _mm_cvtepi32_ps(sign_extended_left); 
    //__m128 left = [L0, L1, L2, L3] packed 32bit floats 
    return left; 
} 

compiles to what you'd expect with gcc5.3またはclang3.7。

これはほとんどのマイクロアーキテクチャでシャッフルスループットのボトルネックになります(Agner Fog's insn tables and microarch pdfおよびタグwikiのその他のリンクを参照)。 SSSE3 pshufbを使用して、各32ビット要素の上半分に符号ビットのコピーを残す必要がある算術右シフトに対して実際のシフト命令を使用するだけで、論理左シフトを実行する価値があります。 AVXなしでpshufbはインプレースでpslldのようにインプレースにシフトしたようです(インテル:(おかげで)、入力の2番目のコピーを作るための余分なMOV命令は避けられます

Skylakeでは、即時ベクトルシフトは、P0/P1上で実行し、そうcvtdq2psん。クロックあたり1台のフロート出力ベクトルにスループットを増加させる左シフトのためpshufbを使用して、シャッフルがポートに5.

事前Skylakeマイクロアーキテクチャを実行しているので、すぐにベクトルのみをシフトHaswellのp0などの単一のポートで実行します。少なくとも、int-> floatと同じポートではありません:Haswellがp1にcvtdq2psを実行するので、pshufbはスループットをクロックあたり1 psのベクトルにします。


ANDマスクなどのように、これを行うより良い方法があるようです。しかし、2シフト、つまりシャッフル+シフトは、32ビットの各要素の下位16ビットを完全な32ビット要素に符号拡張する最良の方法です。

関連する問題