channel_mixer: remap: parse config_chain
[vlc.git] / modules / audio_filter / channel_mixer / remap.c
1 /*****************************************************************************
2  * remap.c : simple channel remapper plug-in
3  *****************************************************************************
4  * Copyright (C) 2011 VLC authors and VideoLAN
5  *
6  * Authors: Cheng Sun <chengsun9@gmail.com>
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_aout.h>
33 #include <vlc_filter.h>
34 #include <vlc_block.h>
35 #include <assert.h>
36
37 /*****************************************************************************
38  * Module descriptor
39  *****************************************************************************/
40 static int  OpenFilter( vlc_object_t * );
41 static void CloseFilter( vlc_object_t * );
42
43 #define REMAP_CFG "aout-remap-"
44
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 };
47
48 static const unsigned channel_idx[]    = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };
49
50 static const char *const channel_name[] =
51 {
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"
57 };
58
59 static const char *const channel_desc[] =
60 {
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" )
64 };
65
66 static const int channel_flag[] =
67 {
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
71 };
72
73 vlc_module_begin ()
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" )
80
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)
88 #undef CHANNEL
89
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 )
93
94     set_callbacks( OpenFilter, CloseFilter )
95
96 vlc_module_end ()
97
98 /*****************************************************************************
99  * Local prototypes
100  *****************************************************************************/
101
102 static block_t *Remap( filter_t *, block_t * );
103
104 typedef void (*remap_fun_t)( filter_t *, const void *, void *,
105                              int, unsigned, unsigned);
106
107 struct filter_sys_t
108 {
109     remap_fun_t pf_remap;
110     int nb_in_ch[AOUT_CHAN_MAX];
111     int8_t map_ch[AOUT_CHAN_MAX];
112     bool b_normalize;
113 };
114
115 static const uint32_t valid_channels[] = {
116 /* list taken from aout_FormatPrintChannels */
117     AOUT_CHAN_LEFT,
118     AOUT_CHAN_RIGHT,
119     AOUT_CHAN_CENTER,
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
137           | AOUT_CHAN_LFE,
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,
154 };
155
156 static inline uint32_t CanonicaliseChannels( uint32_t i_physical_channels )
157 {
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];
161
162     vlc_assert_unreachable();
163 }
164
165 /*****************************************************************************
166  * Remap*: do remapping
167  *****************************************************************************/
168 #define DEFINE_REMAP( name, type ) \
169 static void RemapCopy##name( filter_t *p_filter, \
170                     const void *p_srcorig, void *p_destorig, \
171                     int i_nb_samples, \
172                     unsigned i_nb_in_channels, unsigned i_nb_out_channels ) \
173 { \
174     filter_sys_t *p_sys = ( filter_sys_t * )p_filter->p_sys; \
175     const type *p_src = p_srcorig; \
176     type *p_dest = p_destorig; \
177  \
178     for( int i = 0; i < i_nb_samples; i++ ) \
179     { \
180         for( uint8_t in_ch = 0; in_ch < i_nb_in_channels; in_ch++ ) \
181         { \
182             int8_t out_ch = p_sys->map_ch[ in_ch ]; \
183             if (out_ch < 0) continue; \
184             memcpy( p_dest + out_ch, \
185                     p_src  + in_ch, \
186                     sizeof( type ) ); \
187         } \
188         p_src  += i_nb_in_channels; \
189         p_dest += i_nb_out_channels; \
190     } \
191 } \
192  \
193 static void RemapAdd##name( filter_t *p_filter, \
194                     const void *p_srcorig, void *p_destorig, \
195                     int i_nb_samples, \
196                     unsigned i_nb_in_channels, unsigned i_nb_out_channels ) \
197 { \
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; \
201  \
202     for( int i = 0; i < i_nb_samples; i++ ) \
203     { \
204         for( uint8_t in_ch = 0; in_ch < i_nb_in_channels; in_ch++ ) \
205         { \
206             int8_t out_ch = p_sys->map_ch[ in_ch ]; \
207             if (out_ch < 0) continue; \
208             if( p_sys->b_normalize ) \
209                 p_dest[ out_ch ] += p_src[ in_ch ] / p_sys->nb_in_ch[ out_ch ]; \
210             else \
211                 p_dest[ out_ch ] += p_src[ in_ch ]; \
212         } \
213         p_src  += i_nb_in_channels; \
214         p_dest += i_nb_out_channels; \
215     } \
216 }
217
218 DEFINE_REMAP( U8,   uint8_t  )
219 DEFINE_REMAP( S16N, int16_t  )
220 DEFINE_REMAP( S32N, int32_t  )
221 DEFINE_REMAP( FL32, float    )
222 DEFINE_REMAP( FL64, double   )
223
224 #undef DEFINE_REMAP
225
226 static inline remap_fun_t GetRemapFun( audio_format_t *p_format, bool b_add )
227 {
228     if( b_add )
229     {
230         switch( p_format->i_format )
231         {
232             case VLC_CODEC_U8:
233                 return RemapAddU8;
234             case VLC_CODEC_S16N:
235                 return RemapAddS16N;
236             case VLC_CODEC_S32N:
237                 return RemapAddS32N;
238             case VLC_CODEC_FL32:
239                 return RemapAddFL32;
240             case VLC_CODEC_FL64:
241                 return RemapAddFL64;
242         }
243     }
244     else
245     {
246         switch( p_format->i_format )
247         {
248             case VLC_CODEC_U8:
249                 return RemapCopyU8;
250             case VLC_CODEC_S16N:
251                 return RemapCopyS16N;
252             case VLC_CODEC_S32N:
253                 return RemapCopyS32N;
254             case VLC_CODEC_FL32:
255                 return RemapCopyFL32;
256             case VLC_CODEC_FL64:
257                 return RemapCopyFL64;
258         }
259     }
260
261     return NULL;
262 }
263
264
265 /*****************************************************************************
266  * OpenFilter:
267  *****************************************************************************/
268 static int OpenFilter( vlc_object_t *p_this )
269 {
270     filter_t *p_filter = (filter_t *)p_this;
271     filter_sys_t *p_sys;
272
273     audio_format_t *audio_in  = &p_filter->fmt_in.audio;
274     audio_format_t *audio_out = &p_filter->fmt_out.audio;
275
276     /* Allocate the memory needed to store the module's structure */
277     p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
278     if( unlikely( p_sys == NULL ) )
279         return VLC_ENOMEM;
280
281     static const char *const options[] = {
282         "channel-left", "channel-center", "channel-right", "channel-rearleft",
283         "channel-rearcenter", "channel-rearright", "channel-middleleft",
284         "channel-middleright", "channel-lfe", "normalize",
285     };
286     config_ChainParse(p_filter, REMAP_CFG, options, p_filter->p_cfg);
287
288     /* get number of and layout of input channels */
289     uint32_t i_output_physical = 0;
290     int8_t pi_map_ch[ AOUT_CHAN_MAX ] = { 0 }; /* which out channel each in channel is mapped to */
291     p_sys->b_normalize = var_InheritBool( p_this, REMAP_CFG "normalize" );
292
293     for( uint8_t in_ch = 0, wg4_i = 0; in_ch < audio_in->i_channels; in_ch++, wg4_i++ )
294     {
295         /* explode in_channels in the right order */
296         while( ( audio_in->i_physical_channels & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 )
297         {
298             wg4_i++;
299             assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) );
300         }
301         unsigned channel_wg4idx_len = sizeof( channel_wg4idx )/sizeof( channel_wg4idx[0] );
302         uint8_t *pi_chnidx = memchr( channel_wg4idx, wg4_i, channel_wg4idx_len );
303         assert( pi_chnidx != NULL );
304         uint8_t chnidx = pi_chnidx - channel_wg4idx;
305         int64_t val = var_InheritInteger( p_this, channel_name[chnidx] );
306         if (val >= AOUT_CHAN_MAX)
307         {
308             msg_Err( p_filter, "invalid channel index" );
309             free( p_sys );
310             return VLC_EGENERIC;
311         }
312         if (val < 0)
313         {
314             pi_map_ch[in_ch] = -1;
315             continue;
316         }
317         uint8_t out_idx = val;
318         pi_map_ch[in_ch] = channel_wg4idx[ out_idx ];
319
320         i_output_physical |= channel_flag[ out_idx ];
321     }
322     i_output_physical = CanonicaliseChannels( i_output_physical );
323
324     unsigned i_channels = popcount(i_output_physical);
325
326     /* condense out_channels */
327     uint8_t out_ch_sorted[ AOUT_CHAN_MAX ];
328     for( uint8_t i = 0, wg4_i = 0; i < i_channels; i++, wg4_i++ )
329     {
330         while( ( i_output_physical & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 )
331         {
332             wg4_i++;
333             assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) );
334         }
335         out_ch_sorted[ i ] = wg4_i;
336     }
337     bool b_multiple = false; /* whether we need to add channels (multiple in mapped to an out) */
338     memset( p_sys->nb_in_ch, 0, sizeof( p_sys->nb_in_ch ) );
339     for( uint8_t i = 0; i < audio_in->i_channels; i++ )
340     {
341         int8_t wg4_out_ch = pi_map_ch[i];
342         if (wg4_out_ch < 0)
343         {
344             p_sys->map_ch[i] = -1;
345             continue;
346         }
347         uint8_t *pi_out_ch = memchr( out_ch_sorted, wg4_out_ch, i_channels );
348         assert( pi_out_ch != NULL );
349         p_sys->map_ch[i] = pi_out_ch - out_ch_sorted;
350         if( ++p_sys->nb_in_ch[ p_sys->map_ch[i] ] > 1 )
351             b_multiple = true;
352     }
353
354     p_sys->pf_remap = GetRemapFun( audio_in, b_multiple );
355     if( !p_sys->pf_remap )
356     {
357         msg_Err( p_filter, "Could not decide on %s remap function", b_multiple ? "an add" : "a copy" );
358         free( p_sys );
359         return VLC_EGENERIC;
360     }
361
362     audio_out->i_format = audio_in->i_format;
363     audio_out->i_physical_channels = i_output_physical;
364     aout_FormatPrepare( audio_out );
365
366     msg_Dbg( p_filter, "%s '%4.4s'->'%4.4s' %d Hz->%d Hz %s->%s",
367              "Remap filter",
368              (char *)&audio_in->i_format, (char *)&audio_out->i_format,
369              audio_in->i_rate, audio_out->i_rate,
370              aout_FormatPrintChannels( audio_in ),
371              aout_FormatPrintChannels( audio_out ) );
372
373     p_filter->pf_audio_filter = Remap;
374     return VLC_SUCCESS;
375 }
376
377 /*****************************************************************************
378  * CloseFilter:
379  *****************************************************************************/
380 static void CloseFilter( vlc_object_t *p_this )
381 {
382     filter_t *p_filter = (filter_t *) p_this;
383     filter_sys_t *p_sys = p_filter->p_sys;
384     free( p_sys );
385 }
386
387 /*****************************************************************************
388  * Remap:
389  *****************************************************************************/
390 static block_t *Remap( filter_t *p_filter, block_t *p_block )
391 {
392     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
393     if( !p_block || !p_block->i_nb_samples )
394     {
395         if( p_block )
396             block_Release( p_block );
397         return NULL;
398     }
399
400     size_t i_out_size = p_block->i_nb_samples *
401         p_filter->fmt_out.audio.i_bytes_per_frame;
402
403     block_t *p_out = block_Alloc( i_out_size );
404     if( !p_out )
405     {
406         msg_Warn( p_filter, "can't get output buffer" );
407         block_Release( p_block );
408         return NULL;
409     }
410     p_out->i_nb_samples = p_block->i_nb_samples;
411     p_out->i_dts = p_block->i_dts;
412     p_out->i_pts = p_block->i_pts;
413     p_out->i_length = p_block->i_length;
414
415     memset( p_out->p_buffer, 0, i_out_size );
416
417     p_sys->pf_remap( p_filter,
418                 (const void *)p_block->p_buffer, (void *)p_out->p_buffer,
419                 p_block->i_nb_samples,
420                 p_filter->fmt_in.audio.i_channels,
421                 p_filter->fmt_out.audio.i_channels );
422
423     block_Release( p_block );
424
425     return p_out;
426 }