qt: playlist: use item title if available
[vlc.git] / modules / audio_filter / channel_mixer / simple.c
1 /*****************************************************************************
2  * simple.c : simple channel mixer plug-in
3  *****************************************************************************
4  * Copyright (C) 2002, 2004, 2006-2009 VLC authors and VideoLAN
5  *
6  * Authors: Gildas Bazin <gbazin@videolan.org>
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
36 /*****************************************************************************
37  * Module descriptor
38  *****************************************************************************/
39 static int  OpenFilter( vlc_object_t * );
40
41 vlc_module_begin ()
42     set_description( N_("Audio filter for simple channel mixing") )
43     set_category( CAT_AUDIO )
44     set_subcategory( SUBCAT_AUDIO_AFILTER )
45     set_capability( "audio converter", 10 )
46     set_callback( OpenFilter );
47 vlc_module_end ()
48
49 static block_t *Filter( filter_t *, block_t * );
50
51 static void DoWork_7_x_to_2_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
52     float *p_dest = (float *)p_out_buf->p_buffer;
53     const float *p_src = (const float *)p_in_buf->p_buffer;
54     for( int i = p_in_buf->i_nb_samples; i--; )
55     {
56         float ctr = p_src[6] * 0.7071f;
57         *p_dest++ = ctr + p_src[0] + p_src[2] / 4 + p_src[4] / 4;
58         *p_dest++ = ctr + p_src[1] + p_src[3] / 4 + p_src[5] / 4;
59
60         p_src += 7;
61
62         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
63     }
64 }
65
66 static void DoWork_6_1_to_2_0( filter_t *p_filter, block_t *p_in_buf,
67                                block_t *p_out_buf )
68 {
69     VLC_UNUSED(p_filter);
70     float *p_dest = (float *)p_out_buf->p_buffer;
71     const float *p_src = (const float *)p_in_buf->p_buffer;
72     for( int i = p_in_buf->i_nb_samples; i--; )
73     {
74         float ctr = (p_src[2] + p_src[5]) * 0.7071f;
75         *p_dest++ = p_src[0] + p_src[3] + ctr;
76         *p_dest++ = p_src[1] + p_src[4] + ctr;
77
78         p_src += 6;
79
80         /* We always have LFE here */
81         p_src++;
82     }
83 }
84
85 static void DoWork_5_x_to_2_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
86     float *p_dest = (float *)p_out_buf->p_buffer;
87     const float *p_src = (const float *)p_in_buf->p_buffer;
88     for( int i = p_in_buf->i_nb_samples; i--; )
89     {
90         *p_dest++ = p_src[0] + 0.7071f * (p_src[4] + p_src[2]);
91         *p_dest++ = p_src[1] + 0.7071f * (p_src[4] + p_src[3]);
92
93         p_src += 5;
94
95         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
96     }
97 }
98
99 static void DoWork_4_0_to_2_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
100     VLC_UNUSED(p_filter);
101     float *p_dest = (float *)p_out_buf->p_buffer;
102     const float *p_src = (const float *)p_in_buf->p_buffer;
103     for( int i = p_in_buf->i_nb_samples; i--; )
104     {
105         *p_dest++ = p_src[2] + p_src[3] + 0.5f * p_src[0];
106         *p_dest++ = p_src[2] + p_src[3] + 0.5f * p_src[1];
107         p_src += 4;
108     }
109 }
110
111 static void DoWork_3_x_to_2_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
112     float *p_dest = (float *)p_out_buf->p_buffer;
113     const float *p_src = (const float *)p_in_buf->p_buffer;
114     for( int i = p_in_buf->i_nb_samples; i--; )
115     {
116         *p_dest++ = p_src[2] + 0.5f * p_src[0];
117         *p_dest++ = p_src[2] + 0.5f * p_src[1];
118
119         p_src += 3;
120
121         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
122     }
123 }
124
125 static void DoWork_7_x_to_1_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
126     float *p_dest = (float *)p_out_buf->p_buffer;
127     const float *p_src = (const float *)p_in_buf->p_buffer;
128     for( int i = p_in_buf->i_nb_samples; i--; )
129     {
130         *p_dest++ = p_src[6] + p_src[0] / 4 + p_src[1] / 4 + p_src[2] / 8 + p_src[3] / 8 + p_src[4] / 8 + p_src[5] / 8;
131
132         p_src += 7;
133
134         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
135     }
136 }
137
138 static void DoWork_5_x_to_1_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
139     float *p_dest = (float *)p_out_buf->p_buffer;
140     const float *p_src = (const float *)p_in_buf->p_buffer;
141     for( int i = p_in_buf->i_nb_samples; i--; )
142     {
143         *p_dest++ = 0.7071f * (p_src[0] + p_src[1]) + p_src[4]
144                      + 0.5f * (p_src[2] + p_src[3]);
145
146         p_src += 5;
147
148         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
149     }
150 }
151
152 static void DoWork_4_0_to_1_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
153     VLC_UNUSED(p_filter);
154     float *p_dest = (float *)p_out_buf->p_buffer;
155     const float *p_src = (const float *)p_in_buf->p_buffer;
156     for( int i = p_in_buf->i_nb_samples; i--; )
157     {
158         *p_dest++ = p_src[2] + p_src[3] + p_src[0] / 4 + p_src[1] / 4;
159         p_src += 4;
160     }
161 }
162
163 static void DoWork_3_x_to_1_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
164     float *p_dest = (float *)p_out_buf->p_buffer;
165     const float *p_src = (const float *)p_in_buf->p_buffer;
166     for( int i = p_in_buf->i_nb_samples; i--; )
167     {
168         *p_dest++ = p_src[2] + p_src[0] / 4 + p_src[1] / 4;
169
170         p_src += 3;
171
172         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
173     }
174 }
175
176 static void DoWork_2_x_to_1_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
177     VLC_UNUSED(p_filter);
178     float *p_dest = (float *)p_out_buf->p_buffer;
179     const float *p_src = (const float *)p_in_buf->p_buffer;
180     for( int i = p_in_buf->i_nb_samples; i--; )
181     {
182         *p_dest++ = p_src[0] / 2 + p_src[1] / 2;
183
184         p_src += 2;
185     }
186 }
187
188 static void DoWork_7_x_to_4_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
189     float *p_dest = (float *)p_out_buf->p_buffer;
190     const float *p_src = (const float *)p_in_buf->p_buffer;
191     for( int i = p_in_buf->i_nb_samples; i--; )
192     {
193         *p_dest++ = p_src[6] + 0.5f * p_src[0] + p_src[2] / 6;
194         *p_dest++ = p_src[6] + 0.5f * p_src[1] + p_src[3] / 6;
195         *p_dest++ = p_src[2] / 6 +  p_src[4];
196         *p_dest++ = p_src[3] / 6 +  p_src[5];
197
198         p_src += 7;
199
200         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
201     }
202 }
203
204 static void DoWork_5_x_to_4_0( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
205     float *p_dest = (float *)p_out_buf->p_buffer;
206     const float *p_src = (const float *)p_in_buf->p_buffer;
207     for( int i = p_in_buf->i_nb_samples; i--; )
208     {
209         float ctr = p_src[4] * 0.7071f;
210         *p_dest++ = p_src[0] + ctr;
211         *p_dest++ = p_src[1] + ctr;
212         *p_dest++ = p_src[2];
213         *p_dest++ = p_src[3];
214
215         p_src += 5;
216
217         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
218     }
219 }
220
221 static void DoWork_7_x_to_5_x( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
222     float *p_dest = (float *)p_out_buf->p_buffer;
223     const float *p_src = (const float *)p_in_buf->p_buffer;
224     for( int i = p_in_buf->i_nb_samples; i--; )
225     {
226         *p_dest++ = p_src[0];
227         *p_dest++ = p_src[1];
228         *p_dest++ = (p_src[2] + p_src[4]) * 0.5f;
229         *p_dest++ = (p_src[3] + p_src[5]) * 0.5f;
230         *p_dest++ = p_src[6];
231
232         p_src += 7;
233
234         if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE &&
235             p_filter->fmt_out.audio.i_physical_channels & AOUT_CHAN_LFE )
236             *p_dest++ = *p_src++;
237         else if( p_filter->fmt_in.audio.i_physical_channels & AOUT_CHAN_LFE ) p_src++;
238     }
239 }
240
241 static void DoWork_6_1_to_5_x( filter_t * p_filter,  block_t * p_in_buf, block_t * p_out_buf ) {
242     VLC_UNUSED(p_filter);
243     float *p_dest = (float *)p_out_buf->p_buffer;
244     const float *p_src = (const float *)p_in_buf->p_buffer;
245     for( int i = p_in_buf->i_nb_samples; i--; )
246     {
247         *p_dest++ = p_src[0];
248         *p_dest++ = p_src[1];
249         *p_dest++ = (p_src[2] + p_src[4]) * 0.5f;
250         *p_dest++ = (p_src[3] + p_src[4]) * 0.5f;
251         *p_dest++ = p_src[5];
252
253         p_src += 6;
254
255         /* We always have LFE here */
256         *p_dest++ = *p_src++;
257     }
258 }
259
260 #if defined (CAN_COMPILE_NEON)
261 #include "simple_neon.h"
262 #define GET_WORK(in, out) GET_WORK_##in##_to_##out##_neon()
263 #else
264 #define GET_WORK(in, out) DoWork_##in##_to_##out
265 #endif
266
267 /*****************************************************************************
268  * OpenFilter:
269  *****************************************************************************/
270 static int OpenFilter( vlc_object_t *p_this )
271 {
272     filter_t *p_filter = (filter_t *)p_this;
273     void (*do_work)(filter_t *, block_t *, block_t *) = NULL;
274
275     if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
276         p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format ||
277         p_filter->fmt_in.audio.i_rate != p_filter->fmt_out.audio.i_rate ||
278         aout_FormatNbChannels( &p_filter->fmt_in.audio) < 2 )
279         return VLC_EGENERIC;
280
281     uint32_t input = p_filter->fmt_in.audio.i_physical_channels;
282     uint32_t output = p_filter->fmt_out.audio.i_physical_channels;
283
284     /* Short circuit the common case of not remixing */
285     if( input == output )
286         return VLC_EGENERIC;
287
288     const bool b_input_6_1 = input == AOUT_CHANS_6_1_MIDDLE;
289     const bool b_input_4_center_rear = input == AOUT_CHANS_4_CENTER_REAR;
290
291     input &= ~AOUT_CHAN_LFE;
292
293     const bool b_input_7_x = input == AOUT_CHANS_7_0;
294     const bool b_input_5_x = input == AOUT_CHANS_5_0
295                           || input == AOUT_CHANS_5_0_MIDDLE;
296     const bool b_input_3_x = input == AOUT_CHANS_3_0;
297
298     /*
299      * TODO: We don't support any 8.1 input
300      * TODO: We don't support any 6.x input
301      * TODO: We don't support 4.0 rear and 4.0 middle
302      */
303     if( output == AOUT_CHAN_CENTER )
304     {
305         if( b_input_7_x )
306             do_work = GET_WORK(7_x,1_0);
307         else if( b_input_5_x )
308             do_work = GET_WORK(5_x,1_0);
309         else if( b_input_4_center_rear )
310             do_work = GET_WORK(4_0,1_0);
311         else if( b_input_3_x )
312             do_work = GET_WORK(3_x,1_0);
313         else
314             do_work = GET_WORK(2_x,1_0);
315     }
316     else if( output == AOUT_CHANS_2_0 )
317     {
318         if( b_input_7_x )
319             do_work = GET_WORK(7_x,2_0);
320         else if( b_input_6_1 )
321             do_work = GET_WORK(6_1,2_0);
322         else if( b_input_5_x )
323             do_work = GET_WORK(5_x,2_0);
324         else if( b_input_4_center_rear )
325             do_work = GET_WORK(4_0,2_0);
326         else if( b_input_3_x )
327             do_work = GET_WORK(3_x,2_0);
328     }
329     else if( output == AOUT_CHANS_4_0 )
330     {
331         if( b_input_7_x )
332             do_work = GET_WORK(7_x,4_0);
333         else if( b_input_5_x )
334             do_work = GET_WORK(5_x,4_0);
335     }
336     else if( (output & ~AOUT_CHAN_LFE) == AOUT_CHANS_5_0 ||
337              (output & ~AOUT_CHAN_LFE) == AOUT_CHANS_5_0_MIDDLE )
338     {
339         if( b_input_7_x )
340             do_work = GET_WORK(7_x,5_x);
341         else if( b_input_6_1 )
342             do_work = GET_WORK(6_1,5_x);
343     }
344
345     if( do_work == NULL )
346         return VLC_EGENERIC;
347
348     static const struct vlc_filter_operations filter_ops =
349         { .filter_audio = Filter };
350
351     p_filter->ops = &filter_ops;
352     p_filter->p_sys = do_work;
353     return VLC_SUCCESS;
354 }
355
356 /*****************************************************************************
357  * Filter:
358  *****************************************************************************/
359 static block_t *Filter( filter_t *p_filter, block_t *p_block )
360 {
361     void (*work)(filter_t *, block_t *, block_t *) = (void *)p_filter->p_sys;
362
363     if( !p_block || !p_block->i_nb_samples )
364     {
365         if( p_block )
366             block_Release( p_block );
367         return NULL;
368     }
369
370     size_t i_out_size = p_block->i_nb_samples *
371       p_filter->fmt_out.audio.i_bitspersample *
372         p_filter->fmt_out.audio.i_channels / 8;
373
374     block_t *p_out = block_Alloc( i_out_size );
375     if( !p_out )
376     {
377         msg_Warn( p_filter, "can't get output buffer" );
378         block_Release( p_block );
379         return NULL;
380     }
381
382     p_out->i_nb_samples = p_block->i_nb_samples;
383     p_out->i_dts = p_block->i_dts;
384     p_out->i_pts = p_block->i_pts;
385     p_out->i_length = p_block->i_length;
386
387     int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
388     int i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
389     p_out->i_nb_samples = p_block->i_nb_samples;
390     p_out->i_buffer = p_block->i_buffer * i_output_nb / i_input_nb;
391
392     work( p_filter, p_block, p_out );
393
394     block_Release( p_block );
395
396     return p_out;
397 }