+/*****************************************************************************
+ * aout_CheckChannelReorder : Check if we need to do some channel re-ordering
+ *****************************************************************************/
+int aout_CheckChannelReorder( const uint32_t *pi_chan_order_in,
+ const uint32_t *pi_chan_order_out,
+ uint32_t i_channel_mask,
+ int i_channels, int *pi_chan_table )
+{
+ bool b_chan_reorder = false;
+ int i, j, k, l;
+
+ if( i_channels > AOUT_CHAN_MAX )
+ return false;
+
+ if( pi_chan_order_in == NULL )
+ pi_chan_order_in = pi_vlc_chan_order_wg4;
+ if( pi_chan_order_out == NULL )
+ pi_chan_order_out = pi_vlc_chan_order_wg4;
+
+ for( i = 0, j = 0; pi_chan_order_in[i]; i++ )
+ {
+ if( !(i_channel_mask & pi_chan_order_in[i]) ) continue;
+
+ for( k = 0, l = 0; pi_chan_order_in[i] != pi_chan_order_out[k]; k++ )
+ {
+ if( i_channel_mask & pi_chan_order_out[k] ) l++;
+ }
+
+ pi_chan_table[j++] = l;
+ }
+
+ for( i = 0; i < i_channels; i++ )
+ {
+ if( pi_chan_table[i] != i ) b_chan_reorder = true;
+ }
+
+ return b_chan_reorder;
+}
+
+/*****************************************************************************
+ * aout_ChannelReorder :
+ *****************************************************************************/
+void aout_ChannelReorder( uint8_t *p_buf, int i_buffer,
+ int i_channels, const int *pi_chan_table,
+ int i_bits_per_sample )
+{
+ uint8_t p_tmp[AOUT_CHAN_MAX * 4];
+ int i, j;
+
+ if( i_bits_per_sample == 8 )
+ {
+ for( i = 0; i < i_buffer / i_channels; i++ )
+ {
+ for( j = 0; j < i_channels; j++ )
+ {
+ p_tmp[pi_chan_table[j]] = p_buf[j];
+ }
+
+ memcpy( p_buf, p_tmp, i_channels );
+ p_buf += i_channels;
+ }
+ }
+ else if( i_bits_per_sample == 16 )
+ {
+ for( i = 0; i < i_buffer / i_channels / 2; i++ )
+ {
+ for( j = 0; j < i_channels; j++ )
+ {
+ p_tmp[2 * pi_chan_table[j]] = p_buf[2 * j];
+ p_tmp[2 * pi_chan_table[j] + 1] = p_buf[2 * j + 1];
+ }
+
+ memcpy( p_buf, p_tmp, 2 * i_channels );
+ p_buf += 2 * i_channels;
+ }
+ }
+ else if( i_bits_per_sample == 24 )
+ {
+ for( i = 0; i < i_buffer / i_channels / 3; i++ )
+ {
+ for( j = 0; j < i_channels; j++ )
+ {
+ p_tmp[3 * pi_chan_table[j]] = p_buf[3 * j];
+ p_tmp[3 * pi_chan_table[j] + 1] = p_buf[3 * j + 1];
+ p_tmp[3 * pi_chan_table[j] + 2] = p_buf[3 * j + 2];
+ }
+
+ memcpy( p_buf, p_tmp, 3 * i_channels );
+ p_buf += 3 * i_channels;
+ }
+ }
+ else if( i_bits_per_sample == 32 )
+ {
+ for( i = 0; i < i_buffer / i_channels / 4; i++ )
+ {
+ for( j = 0; j < i_channels; j++ )
+ {
+ p_tmp[4 * pi_chan_table[j]] = p_buf[4 * j];
+ p_tmp[4 * pi_chan_table[j] + 1] = p_buf[4 * j + 1];
+ p_tmp[4 * pi_chan_table[j] + 2] = p_buf[4 * j + 2];
+ p_tmp[4 * pi_chan_table[j] + 3] = p_buf[4 * j + 3];
+ }
+
+ memcpy( p_buf, p_tmp, 4 * i_channels );
+ p_buf += 4 * i_channels;
+ }
+ }
+}
+
+/*****************************************************************************
+ * aout_ChannelExtract:
+ *****************************************************************************/
+static inline void ExtractChannel( uint8_t *pi_dst, int i_dst_channels,
+ const uint8_t *pi_src, int i_src_channels,
+ int i_sample_count,
+ const int *pi_selection, int i_bytes )
+{
+ for( int i = 0; i < i_sample_count; i++ )
+ {
+ for( int j = 0; j < i_dst_channels; j++ )
+ memcpy( &pi_dst[j * i_bytes], &pi_src[pi_selection[j] * i_bytes], i_bytes );
+ pi_dst += i_dst_channels * i_bytes;
+ pi_src += i_src_channels * i_bytes;
+ }
+}
+
+void aout_ChannelExtract( void *p_dst, int i_dst_channels,
+ const void *p_src, int i_src_channels,
+ int i_sample_count, const int *pi_selection, int i_bits_per_sample )
+{
+ /* It does not work in place */
+ assert( p_dst != p_src );
+
+ /* Force the compiler to inline for the specific cases so it can optimize */
+ if( i_bits_per_sample == 8 )
+ ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 1 );
+ else if( i_bits_per_sample == 16 )
+ ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 2 );
+ else if( i_bits_per_sample == 24 )
+ ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 3 );
+ else if( i_bits_per_sample == 32 )
+ ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 4 );
+ else if( i_bits_per_sample == 64 )
+ ExtractChannel( p_dst, i_dst_channels, p_src, i_src_channels, i_sample_count, pi_selection, 8 );
+}
+
+bool aout_CheckChannelExtraction( int *pi_selection,
+ uint32_t *pi_layout, int *pi_channels,
+ const uint32_t pi_order_dst[AOUT_CHAN_MAX],
+ const uint32_t *pi_order_src, int i_channels )
+{
+ const uint32_t pi_order_dual_mono[] = { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT };
+ uint32_t i_layout = 0;
+ int i_out = 0;
+ int pi_index[AOUT_CHAN_MAX];
+
+ /* */
+ if( !pi_order_dst )
+ pi_order_dst = pi_vlc_chan_order_wg4;
+
+ /* Detect special dual mono case */
+ if( i_channels == 2 &&
+ pi_order_src[0] == AOUT_CHAN_CENTER && pi_order_src[1] == AOUT_CHAN_CENTER )
+ {
+ i_layout |= AOUT_CHAN_DUALMONO;
+ pi_order_src = pi_order_dual_mono;
+ }
+
+ /* */
+ for( int i = 0; i < i_channels; i++ )
+ {
+ /* Ignore unknown or duplicated channels or not present in output */
+ if( !pi_order_src[i] || (i_layout & pi_order_src[i]) )
+ continue;
+
+ for( int j = 0; j < AOUT_CHAN_MAX; j++ )
+ {
+ if( pi_order_dst[j] == pi_order_src[i] )
+ {
+ assert( i_out < AOUT_CHAN_MAX );
+ pi_index[i_out++] = i;
+ i_layout |= pi_order_src[i];
+ break;
+ }
+ }
+ }
+
+ /* */
+ for( int i = 0, j = 0; i < AOUT_CHAN_MAX; i++ )
+ {
+ for( int k = 0; k < i_out; k++ )
+ {
+ if( pi_order_dst[i] == pi_order_src[pi_index[k]] )
+ {
+ pi_selection[j++] = pi_index[k];
+ break;
+ }
+ }
+ }
+
+ *pi_layout = i_layout;
+ *pi_channels = i_out;
+
+ for( int i = 0; i < i_out; i++ )
+ {
+ if( pi_selection[i] != i )
+ return true;
+ }
+ return i_out == i_channels;
+}