trivial: accept different chan_modes
[vlc.git] / modules / audio_filter / channel_mixer / trivial.c
blob746934cbed95f060fec4cc7b2fc5fac0651dcff5
1 /*****************************************************************************
2 * trivial.c : trivial channel mixer plug-in (drops unwanted channels)
3 *****************************************************************************
4 * Copyright (C) 2002, 2006, 2014 VLC authors and VideoLAN
6 * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_aout.h>
36 #include <vlc_filter.h>
38 static int Create( vlc_object_t * );
39 static void Destroy( vlc_object_t * );
41 vlc_module_begin ()
42 set_description( N_("Audio filter for trivial channel mixing") )
43 set_capability( "audio converter", 1 )
44 set_category( CAT_AUDIO )
45 set_subcategory( SUBCAT_AUDIO_MISC )
46 set_callbacks( Create, Destroy )
47 vlc_module_end ()
49 struct filter_sys_t
51 int channel_map[AOUT_CHAN_MAX];
54 /**
55 * Trivially upmixes
57 static block_t *Upmix( filter_t *p_filter, block_t *p_in_buf )
59 unsigned i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
60 unsigned i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
62 assert( i_input_nb < i_output_nb );
64 block_t *p_out_buf = block_Alloc(
65 p_in_buf->i_buffer * i_output_nb / i_input_nb );
66 if( unlikely(p_out_buf == NULL) )
68 block_Release( p_in_buf );
69 return NULL;
72 p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
73 p_out_buf->i_dts = p_in_buf->i_dts;
74 p_out_buf->i_pts = p_in_buf->i_pts;
75 p_out_buf->i_length = p_in_buf->i_length;
77 float *p_dest = (float *)p_out_buf->p_buffer;
78 const float *p_src = (float *)p_in_buf->p_buffer;
79 const int *channel_map = p_filter->p_sys->channel_map;
81 for( size_t i = 0; i < p_in_buf->i_nb_samples; i++ )
83 for( unsigned j = 0; j < i_output_nb; j++ )
84 p_dest[j] = channel_map[j] == -1 ? 0.f : p_src[channel_map[j]];
86 p_src += i_input_nb;
87 p_dest += i_output_nb;
90 block_Release( p_in_buf );
91 return p_out_buf;
94 /**
95 * Trivially downmixes (i.e. drop extra channels)
97 static block_t *Downmix( filter_t *p_filter, block_t *p_buf )
99 unsigned i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
100 unsigned i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
102 assert( i_input_nb >= i_output_nb );
104 float *p_dest = (float *)p_buf->p_buffer;
105 const float *p_src = p_dest;
106 const int *channel_map = p_filter->p_sys->channel_map;
107 /* Use an extra buffer to avoid overlapping */
108 float buffer[i_output_nb];
110 for( size_t i = 0; i < p_buf->i_nb_samples; i++ )
112 for( unsigned j = 0; j < i_output_nb; j++ )
113 buffer[j] = channel_map[j] == -1 ? 0.f : p_src[channel_map[j]];
114 memcpy( p_dest, buffer, i_output_nb * sizeof(float) );
116 p_src += i_input_nb;
117 p_dest += i_output_nb;
119 p_buf->i_buffer = p_buf->i_buffer * i_output_nb / i_input_nb;
121 return p_buf;
124 static block_t *Equals( filter_t *p_filter, block_t *p_buf )
126 (void) p_filter;
127 return p_buf;
130 static block_t *Extract( filter_t *p_filter, block_t *p_in_buf )
132 size_t i_out_channels = aout_FormatNbChannels( &p_filter->fmt_out.audio );
133 size_t i_out_size = p_in_buf->i_nb_samples
134 * p_filter->fmt_out.audio.i_bitspersample
135 * i_out_channels / 8;
137 block_t *p_out_buf = block_Alloc( i_out_size );
138 if( unlikely(p_out_buf == NULL) )
140 block_Release( p_in_buf );
141 return NULL;
144 p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
145 p_out_buf->i_dts = p_in_buf->i_dts;
146 p_out_buf->i_pts = p_in_buf->i_pts;
147 p_out_buf->i_length = p_in_buf->i_length;
149 static const int pi_selections[] = {
150 0, 1, 2, 3, 4, 5, 6, 7, 8,
152 static_assert(sizeof(pi_selections)/sizeof(int) == AOUT_CHAN_MAX,
153 "channel max size mismatch!");
155 aout_ChannelExtract( p_out_buf->p_buffer, i_out_channels,
156 p_in_buf->p_buffer, p_filter->fmt_in.audio.i_channels,
157 p_in_buf->i_nb_samples, pi_selections,
158 p_filter->fmt_out.audio.i_bitspersample );
160 return p_out_buf;
164 * Probes the trivial channel mixer
166 static int Create( vlc_object_t *p_this )
168 filter_t *p_filter = (filter_t *)p_this;
169 const audio_format_t *infmt = &p_filter->fmt_in.audio;
170 const audio_format_t *outfmt = &p_filter->fmt_out.audio;
172 if( infmt->i_physical_channels == 0 )
174 assert( infmt->i_channels > 0 );
175 if( outfmt->i_physical_channels == 0 )
176 return VLC_EGENERIC;
177 if( aout_FormatNbChannels( outfmt ) == infmt->i_channels )
179 p_filter->pf_audio_filter = Equals;
180 return VLC_SUCCESS;
182 else
184 if( infmt->i_channels > AOUT_CHAN_MAX )
185 msg_Info(p_filter, "%d channels will be dropped.",
186 infmt->i_channels - AOUT_CHAN_MAX);
187 p_filter->pf_audio_filter = Extract;
188 return VLC_SUCCESS;
192 if( infmt->i_format != outfmt->i_format
193 || infmt->i_rate != outfmt->i_rate
194 || infmt->i_format != VLC_CODEC_FL32 )
195 return VLC_EGENERIC;
197 /* trivial is the lowest priority converter: if chan_mode are different
198 * here, this filter will still need to convert channels (and ignore
199 * chan_mode). */
200 if( infmt->i_physical_channels == outfmt->i_physical_channels
201 && infmt->i_chan_mode == outfmt->i_chan_mode )
202 return VLC_EGENERIC;
204 p_filter->p_sys = NULL;
206 if ( aout_FormatNbChannels( outfmt ) == 1
207 && aout_FormatNbChannels( infmt ) == 1 )
209 p_filter->pf_audio_filter = Equals;
210 return VLC_SUCCESS;
213 /* Setup channel order */
214 uint16_t i_in_physical_channels = infmt->i_physical_channels;
215 uint16_t i_out_physical_channels = outfmt->i_physical_channels;
217 /* Fill src_chans: contains a sorted index of all presents in channels */
218 int i_src_idx = 0;
219 int src_chans[AOUT_CHAN_MAX];
220 for( unsigned i = 0; i < AOUT_CHAN_MAX; ++i )
221 src_chans[i] = pi_vlc_chan_order_wg4[i] & i_in_physical_channels ?
222 i_src_idx++ : -1;
224 unsigned i_dst_idx = 0;
225 int channel_map[AOUT_CHAN_MAX];
226 for( unsigned i = 0; i < AOUT_CHAN_MAX; ++i )
228 const uint32_t i_chan = pi_vlc_chan_order_wg4[i];
229 if( !( i_chan & i_out_physical_channels ) )
230 continue; /* Output channel not present */
232 if( aout_FormatNbChannels( infmt ) == 1 )
234 /* Input is mono, copy the mono channel to Left,Right */
235 if( i_chan & AOUT_CHANS_FRONT )
236 channel_map[i_dst_idx] = 0;
237 else
238 channel_map[i_dst_idx] = -1;
240 else if( src_chans[i] != -1 )
242 /* Input and output have the same channel */
243 assert( i_chan & i_in_physical_channels );
244 channel_map[i_dst_idx] = src_chans[i];
246 else
248 if( ( i_chan & AOUT_CHANS_MIDDLE )
249 && !( i_out_physical_channels & AOUT_CHANS_REAR ) )
251 /* Use Rear chans as Middle chans if Rear chans are not used */
252 assert( i + 2 < AOUT_CHAN_MAX );
253 assert( pi_vlc_chan_order_wg4[i + 2] & AOUT_CHANS_REAR );
254 channel_map[i_dst_idx] = src_chans[i + 2];
256 else if( ( i_chan & AOUT_CHANS_REAR )
257 && !( i_out_physical_channels & AOUT_CHANS_MIDDLE ) )
259 /* Use Middle chans as Rear chans if Middle chans are not used */
260 assert( (int) i - 2 >= 0 );
261 assert( pi_vlc_chan_order_wg4[i - 2] & AOUT_CHANS_MIDDLE );
262 channel_map[i_dst_idx] = src_chans[i - 2];
264 else
265 channel_map[i_dst_idx] = -1;
267 i_dst_idx++;
269 #ifndef NDEBUG
270 for( unsigned i = 0; i < aout_FormatNbChannels( outfmt ); ++i )
272 assert( channel_map[i] == -1
273 || (unsigned) channel_map[i] < aout_FormatNbChannels( infmt ) );
275 #endif
277 if( aout_FormatNbChannels( outfmt ) == aout_FormatNbChannels( infmt ) )
279 /* Channel layouts can be different but the channel order can be the
280 * same. This is the case for AOUT_CHANS_5_1 <-> AOUT_CHANS_5_1_MIDDLE
281 * for example. */
282 bool b_equals = true;
283 for( unsigned i = 0; i < aout_FormatNbChannels( outfmt ); ++i )
284 if( channel_map[i] == -1 || (unsigned) channel_map[i] != i )
286 b_equals = false;
287 break;
289 if( b_equals )
291 p_filter->pf_audio_filter = Equals;
292 return VLC_SUCCESS;
296 p_filter->p_sys = malloc( sizeof(*p_filter->p_sys) );
297 if(! p_filter->p_sys )
298 return VLC_ENOMEM;
299 memcpy( p_filter->p_sys->channel_map, channel_map, sizeof(channel_map) );
301 if( aout_FormatNbChannels( outfmt ) > aout_FormatNbChannels( infmt ) )
302 p_filter->pf_audio_filter = Upmix;
303 else
304 p_filter->pf_audio_filter = Downmix;
306 return VLC_SUCCESS;
309 static void Destroy( vlc_object_t *p_this )
311 filter_t *p_filter = (filter_t *)p_this;
312 free( p_filter->p_sys );