4

オーディオ サンプルの配列に対して (組み込み関数または asm を使用して) 24 ビットから 16 ビットへの量子化を行う高速な方法があるかどうか疑問に思います。

ソース形式は署名済み 24 ファイルです。

更新:説明のように変換を行うことができました:

static void __cdecl Convert24bitToStereo16_SSE2(uint8_t* src, uint8_t* dst, int len)
{
    __m128i shuffleMask = _mm_setr_epi8(-1,0,1,2,-1,3,4,5,-1,6,7,8,-1,9,10,11);             

    __asm 
  {    
        mov        eax, [src]   // src          
        mov        edi, [dst]   // dst
        mov        ecx, [len]   // len

        movdqu     xmm0,xmmword ptr [shuffleMask]           

      convertloop:
        movdqu     xmm1, [eax]              // read 4 samples           
        lea        eax,  [eax + 12]         // inc pointer                      
        pshufb     xmm1,xmm0                // shuffle using mask
        psrldq     xmm1, 2                  // shift right

        movdqu     xmm2, [eax]              // read next 4 samples          
        lea        eax,  [eax + 12]         // inc pointer                      
        pshufb     xmm2, xmm0               // shuffle
        psrldq     xmm2, 2                  // shift right
        packusdw   xmm1, xmm2               // pack upper and lower samples

        movdqu     [edi], xmm1              // write 8 samples
        lea        edi, [edi + 16]
        sub        ecx, 24
        jg         convertloop
  }
}

ディザリングについて - 量子化効果を回避するには?

どんなヒントでも大歓迎です。どうも

4

1 に答える 1

3

最終的なコードは奇妙に見えます。なぜシャッフルしてから、レジスタ全体のバイト単位のシフトを行うのですか? 代わりに、シャッフル コントロール マスクを設定して、最初から適切な場所に配置します。

また、packusdwフルレンジ 32 ビットをフルレンジ 16 ビットに変換しません。2^16-1 より大きい 32 ビット要素を (0xffff に) 飽和させます。したがって、24 ビットのフル レンジから 16 ビットのフル レンジにするには、データを自分で右シフトする必要があります。(オーディオでは、16 ビットから 24 ビットへの変換は、最上位ビットではなく、最下位ビットとして 8 つのゼロ ビットを追加することによって行われます。)

とにかく、これが意味することは、入力の 24 ビットごとの上位 16b を連続してパックしたいということです。シャッフルでこれを行うことができます。

//__m128i shuffleMask = _mm_setr_epi8(-1,0,1,2,-1,3,4,5,-1,6,7,8,-1,9,10,11);
// setr takes its args in reverse order, so right-shift by 2 bytes -> move the first 2 args
//__m128i shiftedMask = _mm_setr_epi8(1,2,-1,3,4,5,-1,6,7,8,-1,9,10,11,-1,-1);

// could get 10B, but packing that into the output would be slower
__m128i mask_lo = _mm_setr_epi8( 1,2,  4,5,   7,8,   10,11,
                                -1,-1, -1,-1, -1,-1, -1,-1);
//    __m128i mask_hi = _mm_setr_epi8(-1,-1, -1,-1, -1,-1, -1,-1,
//                                     1,2,  4,5,   7,8,   10,11);
//  generate this from mask_lo instead of using more storage space  

  ... pointer setup
  movdqu     xmm3, xmmword ptr [mask_lo]
  pshufd     xmm4, xmm3, 0x4E  // swap high/low halves

  convertloop:
    movdqu     xmm0, [eax]              // read 4 samples
    pshufb     xmm0, xmm3               // low 8B = 24->16 of first 12B, high8 = 0
    movdqu     xmm1, [eax + 12]         // read next 4 samples
    pshufb     xmm1, xmm4               // high 8B = 2nd chunk of audio, low8 = 0
    por        xmm1, xmm0               // merge the two halves

    movdqu     [edi], xmm1              // write 8 samples
    add        eax, 24
    lea        edi, [edi + 16]
    sub        ecx, 24
    jg         convertloop

また、配列の末尾を超えて読み取る場合にも注意してください。それぞれmovdquが 16B を読み取りますが、最初の 12 のみを使用します。

同じマスクを 2 回使用することもできPUNPCKLQDQましたが、高値の 8B を低値の 8B を保持するレジスタの上半分に配置していました。ただし、punpck命令は と同じポートを競合しpshufbます。(Nehalem/Sandybridge/IvyBridge ではポート 1、5、Haswell ではポート 5 のみ。) Haswell porでもポート 0、1、5 のいずれかで実行できるため、ポート 5 のボトルネックの問題は発生しません。

Haswell でさえポート 5 を飽和させるためにアンロールしないとループ オーバーヘッドが高すぎますが、それに近い値です。(9 つの融合ドメイン uop、そのうち 2 つはポート 5 を必要とします。ループ運搬依存関係はなく、十分な uop がロード/ストアであり、1 サイクルあたり 4uops が可能です。) Nehalem/Sandybridge/Ivybridge は、2 つのポートでシャッフルできるため、実行ポートでボトルネックになりません。Core2 は に 4 uop かかりPSHUFB、2 サイクルごとに 1 しか維持できませんが、それでもこのデータ移動を行う最速の方法です。Penryn (aka wolfdale) もこれで速いはずですが、詳細は見ていません。ただし、Nehalem より前のバージョンでは、デコーダのスループットが問題になります。

したがって、すべてが L1 キャッシュにある場合、2 サイクルごとに 16B の 16B オーディオを生成できます。(Haswell よりも前のバージョンでは、多少の展開が必要です。)

AMD CPU (Steamroller など) もpshufbと同じポートにpunpckあり、ブール値は他の 2 つのベクトル ポートのいずれかで実行できるため、同じ状況です。シャッフルは Intel よりもレイテンシが高くなりますが、スループットは依然として 1 サイクルあたり 1 です。

切り捨てではなく適切な丸めが必要な場合は、切り捨ての前にサンプルに 2^7 などを追加します。(おそらく符号調整が必要です。) ディザリングが必要な場合は、さらに複雑なものが必要です。それをググるか、ライブラリの実装を探す必要があります。Audacity はオープンソースなので、彼らがどのようにそれを行っているかを見ることができます。

于 2015-07-08T22:48:36.717 に答える