1 /*****************************************************************************
2 * remap.c : simple channel remapper plug-in
3 *****************************************************************************
4 * Copyright (C) 2011 VLC authors and VideoLAN
6 * Authors: Cheng Sun <chengsun9@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_block.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int OpenFilter( vlc_object_t * );
41 static void CloseFilter( vlc_object_t * );
43 #define REMAP_CFG "aout-remap-"
45 /* wg4 channel indices in the order of channel_name */
46 static const uint8_t channel_wg4idx[] = { 0, 7, 1, 4, 6, 5, 2, 3, 8 };
48 static const unsigned channel_idx[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
50 static const char *const channel_name[] =
52 REMAP_CFG "channel-left", REMAP_CFG "channel-center",
53 REMAP_CFG "channel-right", REMAP_CFG "channel-rearleft",
54 REMAP_CFG "channel-rearcenter", REMAP_CFG "channel-rearright",
55 REMAP_CFG "channel-middleleft", REMAP_CFG "channel-middleright",
56 REMAP_CFG "channel-lfe"
59 static const char *const channel_desc[] =
61 N_( "Left" ), N_( "Center" ), N_( "Right" ),
62 N_( "Rear left" ), N_( "Rear center" ), N_( "Rear right" ),
63 N_( "Side left" ), N_( "Side right" ), N_( "Low-frequency effects" )
66 static const int channel_flag[] =
68 AOUT_CHAN_LEFT, AOUT_CHAN_CENTER, AOUT_CHAN_RIGHT,
69 AOUT_CHAN_REARLEFT, AOUT_CHAN_REARCENTER, AOUT_CHAN_REARRIGHT,
70 AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, AOUT_CHAN_LFE
74 set_description( N_("Audio channel remapper") )
75 set_capability( "audio filter", 0 )
76 set_category( CAT_AUDIO )
77 set_subcategory( SUBCAT_AUDIO_AFILTER )
78 set_callbacks( OpenFilter, CloseFilter )
79 set_shortname( "Remap" )
81 #define CHANNEL( idx ) \
82 add_integer( channel_name[idx], idx, channel_desc[idx], \
83 channel_desc[idx], false) \
84 change_integer_list( channel_idx, channel_desc )
85 CHANNEL(0) CHANNEL(1) CHANNEL(2)
86 CHANNEL(3) CHANNEL(4) CHANNEL(5)
87 CHANNEL(6) CHANNEL(7) CHANNEL(8)
90 add_bool( REMAP_CFG "normalize", true, "Normalize channels",
91 "When mapping more than one channel to a single output channel, "
92 "normalize the output accordingly.", false )
94 set_callbacks( OpenFilter, CloseFilter )
98 /*****************************************************************************
100 *****************************************************************************/
102 static block_t *Remap( filter_t *, block_t * );
104 typedef void (*remap_fun_t)( filter_t *, const void *, void *,
105 int, unsigned, unsigned);
109 remap_fun_t pf_remap;
110 int nb_in_ch[AOUT_CHAN_MAX];
111 uint8_t map_ch[AOUT_CHAN_MAX];
115 static const uint32_t valid_channels[] = {
116 /* list taken from aout_FormatPrintChannels */
120 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT,
121 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER,
122 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER,
123 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
124 | AOUT_CHAN_REARCENTER,
125 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
126 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
127 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
128 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT,
129 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
130 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT,
131 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
132 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT,
133 AOUT_CHAN_CENTER | AOUT_CHAN_LFE,
134 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_LFE,
135 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_LFE,
136 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER
138 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
139 | AOUT_CHAN_REARCENTER | AOUT_CHAN_LFE,
140 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
141 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE,
142 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
143 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
144 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
145 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE,
146 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
147 | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
148 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
149 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT
150 | AOUT_CHAN_MIDDLERIGHT,
151 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
152 | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT
153 | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE,
156 static inline uint32_t CanonicaliseChannels( uint32_t i_physical_channels )
158 for( unsigned i = 0; i < sizeof( valid_channels )/sizeof( valid_channels[0] ); i++ )
159 if( (i_physical_channels & ~valid_channels[i]) == 0 )
160 return valid_channels[i];
166 /*****************************************************************************
167 * Remap*: do remapping
168 *****************************************************************************/
169 #define DEFINE_REMAP( name, type ) \
170 static void RemapCopy##name( filter_t *p_filter, \
171 const void *p_srcorig, void *p_destorig, \
173 unsigned i_nb_in_channels, unsigned i_nb_out_channels ) \
175 filter_sys_t *p_sys = ( filter_sys_t * )p_filter->p_sys; \
176 const type *p_src = p_srcorig; \
177 type *p_dest = p_destorig; \
179 for( int i = 0; i < i_nb_samples; i++ ) \
181 for( uint8_t in_ch = 0; in_ch < i_nb_in_channels; in_ch++ ) \
183 uint8_t out_ch = p_sys->map_ch[ in_ch ]; \
184 memcpy( p_dest + out_ch, \
188 p_src += i_nb_in_channels; \
189 p_dest += i_nb_out_channels; \
193 static void RemapAdd##name( filter_t *p_filter, \
194 const void *p_srcorig, void *p_destorig, \
196 unsigned i_nb_in_channels, unsigned i_nb_out_channels ) \
198 filter_sys_t *p_sys = ( filter_sys_t * )p_filter->p_sys; \
199 const type *p_src = p_srcorig; \
200 type *p_dest = p_destorig; \
202 for( int i = 0; i < i_nb_samples; i++ ) \
204 for( uint8_t in_ch = 0; in_ch < i_nb_in_channels; in_ch++ ) \
206 uint8_t out_ch = p_sys->map_ch[ in_ch ]; \
207 if( p_sys->b_normalize ) \
208 p_dest[ out_ch ] += p_src[ in_ch ] / p_sys->nb_in_ch[ out_ch ]; \
210 p_dest[ out_ch ] += p_src[ in_ch ]; \
212 p_src += i_nb_in_channels; \
213 p_dest += i_nb_out_channels; \
217 DEFINE_REMAP( U8, uint8_t )
218 DEFINE_REMAP( S16N, int16_t )
219 DEFINE_REMAP( S32N, int32_t )
220 DEFINE_REMAP( FL32, float )
221 DEFINE_REMAP( FL64, double )
225 static inline remap_fun_t GetRemapFun( audio_format_t *p_format, bool b_add )
229 switch( p_format->i_format )
245 switch( p_format->i_format )
250 return RemapCopyS16N;
252 return RemapCopyS32N;
254 return RemapCopyFL32;
256 return RemapCopyFL64;
264 /*****************************************************************************
266 *****************************************************************************/
267 static int OpenFilter( vlc_object_t *p_this )
269 filter_t *p_filter = (filter_t *)p_this;
272 audio_format_t *audio_in = &p_filter->fmt_in.audio;
273 audio_format_t *audio_out = &p_filter->fmt_out.audio;
275 if( ( audio_in->i_format != audio_out->i_format ) ||
276 ( audio_in->i_rate != audio_out->i_rate ) )
279 /* Allocate the memory needed to store the module's structure */
280 p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
281 if( unlikely( p_sys == NULL ) )
284 /* get number of and layout of input channels */
285 uint32_t i_output_physical = 0;
286 uint8_t pi_map_ch[ AOUT_CHAN_MAX ] = { 0 }; /* which out channel each in channel is mapped to */
287 p_sys->b_normalize = var_InheritBool( p_this, REMAP_CFG "normalize" );
289 for( uint8_t in_ch = 0, wg4_i = 0; in_ch < audio_in->i_channels; in_ch++, wg4_i++ )
291 /* explode in_channels in the right order */
292 while( ( audio_in->i_physical_channels & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 )
295 assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) );
297 unsigned channel_wg4idx_len = sizeof( channel_wg4idx )/sizeof( channel_wg4idx[0] );
298 uint8_t *pi_chnidx = memchr( channel_wg4idx, wg4_i, channel_wg4idx_len );
299 assert( pi_chnidx != NULL );
300 uint8_t chnidx = pi_chnidx - channel_wg4idx;
301 uint8_t out_idx = var_InheritInteger( p_this, channel_name[chnidx] );
302 pi_map_ch[in_ch] = channel_wg4idx[ out_idx ];
304 i_output_physical |= channel_flag[ out_idx ];
306 i_output_physical = CanonicaliseChannels( i_output_physical );
308 audio_out->i_physical_channels = i_output_physical;
309 aout_FormatPrepare( audio_out );
311 /* condense out_channels */
312 uint8_t out_ch_sorted[ AOUT_CHAN_MAX ];
313 for( uint8_t i = 0, wg4_i = 0; i < audio_out->i_channels; i++, wg4_i++ )
315 while( ( audio_out->i_physical_channels & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 )
318 assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) );
320 out_ch_sorted[ i ] = wg4_i;
322 bool b_multiple = false; /* whether we need to add channels (multiple in mapped to an out) */
323 memset( p_sys->nb_in_ch, 0, sizeof( p_sys->nb_in_ch ) );
324 for( uint8_t i = 0; i < audio_in->i_channels; i++ )
326 uint8_t wg4_out_ch = pi_map_ch[i];
327 uint8_t *pi_out_ch = memchr( out_ch_sorted, wg4_out_ch, audio_out->i_channels );
328 assert( pi_out_ch != NULL );
329 p_sys->map_ch[i] = pi_out_ch - out_ch_sorted;
330 if( ++p_sys->nb_in_ch[ p_sys->map_ch[i] ] > 1 )
334 msg_Dbg( p_filter, "%s '%4.4s'->'%4.4s' %d Hz->%d Hz %s->%s",
336 (char *)&audio_in->i_format, (char *)&audio_out->i_format,
337 audio_in->i_rate, audio_out->i_rate,
338 aout_FormatPrintChannels( audio_in ),
339 aout_FormatPrintChannels( audio_out ) );
341 p_sys->pf_remap = GetRemapFun( audio_in, b_multiple );
342 if( !p_sys->pf_remap )
344 msg_Err( p_filter, "Could not decide on %s remap function", b_multiple ? "an add" : "a copy" );
349 p_filter->pf_audio_filter = Remap;
353 /*****************************************************************************
355 *****************************************************************************/
356 static void CloseFilter( vlc_object_t *p_this )
358 filter_t *p_filter = (filter_t *) p_this;
359 filter_sys_t *p_sys = p_filter->p_sys;
363 /*****************************************************************************
365 *****************************************************************************/
366 static block_t *Remap( filter_t *p_filter, block_t *p_block )
368 filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
369 if( !p_block || !p_block->i_nb_samples )
372 block_Release( p_block );
376 size_t i_out_size = p_block->i_nb_samples *
377 p_filter->fmt_out.audio.i_bytes_per_frame;
379 block_t *p_out = block_Alloc( i_out_size );
382 msg_Warn( p_filter, "can't get output buffer" );
383 block_Release( p_block );
386 p_out->i_nb_samples = p_block->i_nb_samples;
387 p_out->i_dts = p_block->i_dts;
388 p_out->i_pts = p_block->i_pts;
389 p_out->i_length = p_block->i_length;
391 memset( p_out->p_buffer, 0, i_out_size );
393 p_sys->pf_remap( p_filter,
394 (const void *)p_block->p_buffer, (void *)p_out->p_buffer,
395 p_block->i_nb_samples,
396 p_filter->fmt_in.audio.i_channels,
397 p_filter->fmt_out.audio.i_channels );
399 block_Release( p_block );