2016-08-28 10 views
1

Я использую звуковой микшер, он отлично работает без SIMD-инструкций, но с трудом выясняет, как извлекать мои звуковые данные на отдельные каналы.Переключить аудиоканалы с помощью SIMD-команд

Мои данные поступают в чередующемся формате: L0R0 L1R1 L2R2 L3R3 ... Я загружаю их в __m128i в том же формате, поэтому у меня есть 4 образца в регистре.

Я хотел бы, чтобы они были в разных каналах: L0L1L2L3 R0R1R2R3. Это та часть, которую мне не хватает.

Таким образом, вход: 8 x i16 (4xi32 чередующийся) Я хотел бы, чтобы выход как 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 х i32-х, то было бы легко преобразовать их в f32-s с _mm_cvtepi32_ps и я бы сделал.

Спасибо.

+0

Я думаю, что ваш лучший выбор для преобразования низких/высоких 'i16' с бит-сдвигами. 'high_halves = _mm_srai_epi32 (упакованный, 16);'.Поскольку ваши значения подписаны, вам, вероятно, нужно подписать - продлить низкие половинки с помощью сдвига влево и затем использовать арифметический сдвиг вправо. Я не могу думать о лучшем банкомате, но это кажется неуклюжим. –

+0

Некоторые наборы инструкций SIMD (например, ARM NEON/ARMv8) содержат больше инструкций с двумя входами или двумя выводами, и я думаю, что это можно было бы распаковать с помощью одной команды. (Возможно, распакуйте, IIRC). Поэтому важно сказать конкретно с Intel SSE, а не только с SIMD. –

+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/) для современных ПК, это очень часто (для геймеров Steam это 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 и другие ссылки в теге ). Возможно, стоит использовать SSSE3 pshufb, чтобы выполнить логический сдвиг влево, только используя фактические инструкции сдвига для арифметических сдвигов вправо, которые должны оставить копию знакового бита в верхней половине каждого 32-битного элемента. Без AVX, pshufb перемешивает на месте, так же, как pslld сдвигов в месте (спасибо, Intel :(), так что не избежать дополнительной инструкции MOV, чтобы сделать 2-ую копию входного сигнала.

На Skylake, непосредственные вектор смещения работать на p0/p1, и так же cvtdq2ps. Использование pshufb для сдвига влево приведет к увеличению пропускной способности для вывода вектора один флоат за такт, так как перемешивает работает на порту 5.

Pre-skylake, непосредственный вектор смещается только запускается на одном порту, например, p0 в Haswell. По крайней мере, это не тот же порт, что и int-> float: Haswell запускает cvtdq2ps на p1. Таким образом, pshufb увеличит пропускную способность до одного ps вектора за такт.


Похоже, что должен быть лучший способ сделать это, например, с помощью маски И или чего-то еще. Но кажется, что 2 смены или сдвиг + сдвиг - лучший способ подписать - продлить низкие 16 бит каждого 32-битного элемента в полный 32-разрядный элемент.

 Смежные вопросы

  • Нет связанных вопросов^_^