我实现了混音器,它在没有SIMD指令的情况下运行良好,但很难确定如何将声音数据提取到单独的通道中。
我的数据采用交错格式:L0R0 L1R1 L2R2 L3R3 ...... 我以相同的格式将它们加载到__m128i中,所以我在寄存器中有4个样本。
我希望它们位于不同的通道中:L0L1L2L3 R0R1R2R3。这是我错过的部分。
所以输入为:8 x i16(4xi32 interleaved) 我希望输出为left = 4 x f32和right = 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 x i32-s,那么使用_mm_cvtepi32_ps将它们转换为f32-s很容易,我就完成了。
感谢。
答案 0 :(得分: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以及x86标记wiki中的其他链接)。使用SSSE3 pshufb
进行逻辑左移可能是值得的,仅使用实际移位指令进行算术右移,需要在每个32位元素的上半部分留下符号位的副本。没有AVX,pshufb
就地移动,就像pslld
就地移位一样(谢谢,英特尔:()),所以它不会避免额外的MOV指令来输入第二个副本
在Skylake上,立即向量移位在p0 / p1上运行,cvtdq2ps
也是如此。使用pshufb
进行左移将使吞吐量增加到每个时钟一个浮点输出向量,因为随机数在端口5上运行。
Pre-skylake,立即向量移位仅在单个端口上运行,例如哈斯威尔的p0。至少那个与int-&gt; float不同的端口:Haswell在p1上运行cvtdq2ps
。同样,pshufb会将吞吐量提高到每个时钟一个ps矢量。
似乎应该有更好的方法来做到这一点,比如使用AND面具或其他东西。但似乎2个移位或shuffle +移位是将每个32位元素的低16位符号扩展为完整32位元素的最佳方式。