qt: playlist: use item title if available
[vlc.git] / modules / audio_filter / channel_mixer / mono.c
1 /*****************************************************************************
2  * mono.c : stereo2mono downmixsimple channel mixer plug-in
3  *****************************************************************************
4  * Copyright (C) 2006 M2X
5  *
6  * Authors: Jean-Paul Saman <jpsaman at m2x dot nl>
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 <math.h>                                        /* sqrt */
31 #include <stdint.h>                                         /* int16_t .. */
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_block.h>
36 #include <vlc_filter.h>
37 #include <vlc_aout.h>
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  OpenFilter    ( vlc_object_t * );
43 static void CloseFilter   ( filter_t * );
44
45 static block_t *Convert( filter_t *p_filter, block_t *p_block );
46
47 static unsigned int stereo_to_mono( filter_t *, block_t *, block_t * );
48 static unsigned int mono( filter_t *, block_t *, block_t * );
49 static void stereo2mono_downmix( filter_t *, block_t *, block_t * );
50
51 /*****************************************************************************
52  * Local structures
53  *****************************************************************************/
54 struct atomic_operation_t
55 {
56     int i_source_channel_offset;
57     int i_dest_channel_offset;
58     unsigned int i_delay;/* in sample unit */
59     double d_amplitude_factor;
60 };
61
62 typedef struct
63 {
64     bool b_downmix;
65
66     unsigned int i_nb_channels; /* number of int16_t per sample */
67     int i_channel_selected;
68     int i_bitspersample;
69
70     size_t i_overflow_buffer_size;/* in bytes */
71     uint8_t * p_overflow_buffer;
72     unsigned int i_nb_atomic_operations;
73     struct atomic_operation_t * p_atomic_operations;
74 } filter_sys_t;
75
76 #define MONO_DOWNMIX_TEXT N_("Use downmix algorithm")
77 #define MONO_DOWNMIX_LONGTEXT N_("This option selects a stereo to mono " \
78     "downmix algorithm that is used in the headphone channel mixer. It " \
79     "gives the effect of standing in a room full of speakers." )
80
81 #define MONO_CHANNEL_TEXT N_("Select channel to keep")
82 #define MONO_CHANNEL_LONGTEXT N_("This option silences all other channels " \
83     "except the selected channel.")
84
85 static const int pi_pos_values[] = { 0, 1, 4, 5, 7, 8, 2, 3, 6 };
86 static const char *const ppsz_pos_descriptions[] =
87 { N_("Left"), N_("Right"),
88   N_("Rear left"), N_("Rear right"),
89   N_("Center"), N_("Low-frequency effects"),
90   N_("Side left"), N_("Side right"), N_("Rear center") };
91
92 #define MONO_CFG "sout-mono-"
93 /*****************************************************************************
94  * Module descriptor
95  *****************************************************************************/
96 vlc_module_begin ()
97     set_description( N_("Stereo to mono downmixer") )
98     set_capability( "audio filter", 0 )
99     set_category( CAT_AUDIO )
100     set_subcategory( SUBCAT_AUDIO_AFILTER )
101     set_callback( OpenFilter )
102     set_shortname( "Mono" )
103
104     add_bool( MONO_CFG "downmix", true, MONO_DOWNMIX_TEXT,
105               MONO_DOWNMIX_LONGTEXT, false )
106     add_integer( MONO_CFG "channel", -1, MONO_CHANNEL_TEXT,
107         MONO_CHANNEL_LONGTEXT, false )
108         change_integer_list( pi_pos_values, ppsz_pos_descriptions )
109
110 vlc_module_end ()
111
112 /* Init() and ComputeChannelOperations() -
113  * Code taken from modules/audio_filter/channel_mixer/headphone.c
114  * converted from float into int16_t based downmix
115  * Written by Boris Dorès <babal@via.ecp.fr>
116  */
117
118 /*****************************************************************************
119  * Init: initialize internal data structures
120  * and computes the needed atomic operations
121  *****************************************************************************/
122 /* x and z represent the coordinates of the virtual speaker
123  *  relatively to the center of the listener's head, measured in meters :
124  *
125  *  left              right
126  *Z
127  *-
128  *a          head
129  *x
130  *i
131  *s
132  *  rear left    rear right
133  *
134  *          x-axis
135  *  */
136 static void ComputeChannelOperations( filter_sys_t * p_data,
137         unsigned int i_rate, unsigned int i_next_atomic_operation,
138         int i_source_channel_offset, double d_x, double d_z,
139         double d_compensation_length, double d_channel_amplitude_factor )
140 {
141     double d_c = 340; /*sound celerity (unit: m/s)*/
142     double d_compensation_delay = (d_compensation_length-0.1) / d_c * i_rate;
143
144     /* Left ear */
145     p_data->p_atomic_operations[i_next_atomic_operation]
146         .i_source_channel_offset = i_source_channel_offset;
147     p_data->p_atomic_operations[i_next_atomic_operation]
148         .i_dest_channel_offset = 0;/* left */
149     p_data->p_atomic_operations[i_next_atomic_operation]
150         .i_delay = (int)( sqrt( (-0.1-d_x)*(-0.1-d_x) + (0-d_z)*(0-d_z) )
151                           / d_c * i_rate - d_compensation_delay );
152     if( d_x < 0 )
153     {
154         p_data->p_atomic_operations[i_next_atomic_operation]
155             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
156     }
157     else if( d_x > 0 )
158     {
159         p_data->p_atomic_operations[i_next_atomic_operation]
160             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
161     }
162     else
163     {
164         p_data->p_atomic_operations[i_next_atomic_operation]
165             .d_amplitude_factor = d_channel_amplitude_factor / 2;
166     }
167
168     /* Right ear */
169     p_data->p_atomic_operations[i_next_atomic_operation + 1]
170         .i_source_channel_offset = i_source_channel_offset;
171     p_data->p_atomic_operations[i_next_atomic_operation + 1]
172         .i_dest_channel_offset = 1;/* right */
173     p_data->p_atomic_operations[i_next_atomic_operation + 1]
174         .i_delay = (int)( sqrt( (0.1-d_x)*(0.1-d_x) + (0-d_z)*(0-d_z) )
175                           / d_c * i_rate - d_compensation_delay );
176     if( d_x < 0 )
177     {
178         p_data->p_atomic_operations[i_next_atomic_operation + 1]
179             .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
180     }
181     else if( d_x > 0 )
182     {
183         p_data->p_atomic_operations[i_next_atomic_operation + 1]
184             .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
185     }
186     else
187     {
188         p_data->p_atomic_operations[i_next_atomic_operation + 1]
189             .d_amplitude_factor = d_channel_amplitude_factor / 2;
190     }
191 }
192
193 static int Init( vlc_object_t *p_this, filter_sys_t * p_data,
194                  unsigned int i_nb_channels, uint32_t i_physical_channels,
195                  unsigned int i_rate )
196 {
197     double d_x = var_InheritInteger( p_this, "headphone-dim" );
198     double d_z = d_x;
199     double d_z_rear = -d_x/3;
200     double d_min = 0;
201     unsigned int i_next_atomic_operation;
202     int i_source_channel_offset;
203     unsigned int i;
204
205     if( var_InheritBool( p_this, "headphone-compensate" ) )
206     {
207         /* minimal distance to any speaker */
208         if( i_physical_channels & AOUT_CHAN_REARCENTER )
209         {
210             d_min = d_z_rear;
211         }
212         else
213         {
214             d_min = d_z;
215         }
216     }
217
218     /* Number of elementary operations */
219     p_data->i_nb_atomic_operations = i_nb_channels * 2;
220     if( i_physical_channels & AOUT_CHAN_CENTER )
221     {
222         p_data->i_nb_atomic_operations += 2;
223     }
224     p_data->p_atomic_operations = malloc( sizeof(struct atomic_operation_t)
225             * p_data->i_nb_atomic_operations );
226     if( p_data->p_atomic_operations == NULL )
227         return -1;
228
229     /* For each virtual speaker, computes elementary wave propagation time
230      * to each ear */
231     i_next_atomic_operation = 0;
232     i_source_channel_offset = 0;
233     if( i_physical_channels & AOUT_CHAN_LEFT )
234     {
235         ComputeChannelOperations( p_data , i_rate
236                 , i_next_atomic_operation , i_source_channel_offset
237                 , -d_x , d_z , d_min , 2.0 / i_nb_channels );
238         i_next_atomic_operation += 2;
239         i_source_channel_offset++;
240     }
241     if( i_physical_channels & AOUT_CHAN_RIGHT )
242     {
243         ComputeChannelOperations( p_data , i_rate
244                 , i_next_atomic_operation , i_source_channel_offset
245                 , d_x , d_z , d_min , 2.0 / i_nb_channels );
246         i_next_atomic_operation += 2;
247         i_source_channel_offset++;
248     }
249     if( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
250     {
251         ComputeChannelOperations( p_data , i_rate
252                 , i_next_atomic_operation , i_source_channel_offset
253                 , -d_x , 0 , d_min , 1.5 / i_nb_channels );
254         i_next_atomic_operation += 2;
255         i_source_channel_offset++;
256     }
257     if( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
258     {
259         ComputeChannelOperations( p_data , i_rate
260                 , i_next_atomic_operation , i_source_channel_offset
261                 , d_x , 0 , d_min , 1.5 / i_nb_channels );
262         i_next_atomic_operation += 2;
263         i_source_channel_offset++;
264     }
265     if( i_physical_channels & AOUT_CHAN_REARLEFT )
266     {
267         ComputeChannelOperations( p_data , i_rate
268                 , i_next_atomic_operation , i_source_channel_offset
269                 , -d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
270         i_next_atomic_operation += 2;
271         i_source_channel_offset++;
272     }
273     if( i_physical_channels & AOUT_CHAN_REARRIGHT )
274     {
275         ComputeChannelOperations( p_data , i_rate
276                 , i_next_atomic_operation , i_source_channel_offset
277                 , d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
278         i_next_atomic_operation += 2;
279         i_source_channel_offset++;
280     }
281     if( i_physical_channels & AOUT_CHAN_REARCENTER )
282     {
283         ComputeChannelOperations( p_data , i_rate
284                 , i_next_atomic_operation , i_source_channel_offset
285                 , 0 , -d_z , d_min , 1.5 / i_nb_channels );
286         i_next_atomic_operation += 2;
287         i_source_channel_offset++;
288     }
289     if( i_physical_channels & AOUT_CHAN_CENTER )
290     {
291         /* having two center channels increases the spatialization effect */
292         ComputeChannelOperations( p_data , i_rate
293                 , i_next_atomic_operation , i_source_channel_offset
294                 , d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
295         i_next_atomic_operation += 2;
296         ComputeChannelOperations( p_data , i_rate
297                 , i_next_atomic_operation , i_source_channel_offset
298                 , -d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
299         i_next_atomic_operation += 2;
300         i_source_channel_offset++;
301     }
302     if( i_physical_channels & AOUT_CHAN_LFE )
303     {
304         ComputeChannelOperations( p_data , i_rate
305                 , i_next_atomic_operation , i_source_channel_offset
306                 , 0 , d_z_rear , d_min , 5.0 / i_nb_channels );
307         i_next_atomic_operation += 2;
308         i_source_channel_offset++;
309     }
310
311     /* Initialize the overflow buffer
312      * we need it because the process induce a delay in the samples */
313     p_data->i_overflow_buffer_size = 0;
314     for( i = 0 ; i < p_data->i_nb_atomic_operations ; i++ )
315     {
316         if( p_data->i_overflow_buffer_size
317                 < p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t) )
318         {
319             p_data->i_overflow_buffer_size
320                 = p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t);
321         }
322     }
323     p_data->p_overflow_buffer = malloc( p_data->i_overflow_buffer_size );
324     if( p_data->p_overflow_buffer == NULL )
325     {
326         free( p_data->p_atomic_operations );
327         return -1;
328     }
329     memset( p_data->p_overflow_buffer, 0, p_data->i_overflow_buffer_size );
330
331     /* end */
332     return 0;
333 }
334
335 /*****************************************************************************
336  * OpenFilter
337  *****************************************************************************/
338 static int OpenFilter( vlc_object_t *p_this )
339 {
340     filter_t * p_filter = (filter_t *)p_this;
341     filter_sys_t *p_sys = NULL;
342
343     if( aout_FormatNbChannels( &(p_filter->fmt_in.audio) ) == 1 )
344     {
345         /*msg_Dbg( p_filter, "filter discarded (incompatible format)" );*/
346         return VLC_EGENERIC;
347     }
348
349     /* Allocate the memory needed to store the module's structure */
350     p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
351     if( p_sys == NULL )
352         return VLC_EGENERIC;
353
354     p_sys->b_downmix = var_InheritBool( p_this, MONO_CFG "downmix" );
355     p_sys->i_channel_selected = var_InheritInteger( p_this, MONO_CFG "channel" );
356
357     p_sys->i_nb_channels = aout_FormatNbChannels( &(p_filter->fmt_in.audio) );
358     p_sys->i_bitspersample = p_filter->fmt_out.audio.i_bitspersample;
359
360     p_sys->i_overflow_buffer_size = 0;
361     p_sys->p_overflow_buffer = NULL;
362     p_sys->i_nb_atomic_operations = 0;
363     p_sys->p_atomic_operations = NULL;
364
365     if( Init( VLC_OBJECT(p_filter), p_filter->p_sys,
366               aout_FormatNbChannels( &p_filter->fmt_in.audio ),
367               p_filter->fmt_in.audio.i_physical_channels,
368               p_filter->fmt_in.audio.i_rate ) < 0 )
369     {
370         free( p_sys );
371         return VLC_EGENERIC;
372     }
373
374     if( p_sys->b_downmix )
375     {
376         msg_Dbg( p_this, "using stereo to mono downmix" );
377         p_filter->fmt_out.audio.i_physical_channels = AOUT_CHAN_CENTER;
378         p_filter->fmt_out.audio.i_channels = 1;
379     }
380     else
381     {
382         msg_Dbg( p_this, "using pseudo mono" );
383         p_filter->fmt_out.audio.i_physical_channels = AOUT_CHANS_STEREO;
384         p_filter->fmt_out.audio.i_channels = 2;
385     }
386     p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
387
388     static const struct vlc_filter_operations filter_ops =
389     {
390         .filter_audio = Convert, .close = CloseFilter,
391     };
392     p_filter->ops = &filter_ops;
393
394     msg_Dbg( p_this, "%4.4s->%4.4s, channels %d->%d, bits per sample: %i->%i",
395              (char *)&p_filter->fmt_in.i_codec,
396              (char *)&p_filter->fmt_out.i_codec,
397              p_filter->fmt_in.audio.i_physical_channels,
398              p_filter->fmt_out.audio.i_physical_channels,
399              p_filter->fmt_in.audio.i_bitspersample,
400              p_filter->fmt_out.audio.i_bitspersample );
401
402     p_filter->fmt_in.audio.i_format = VLC_CODEC_S16N;
403     aout_FormatPrepare(&p_filter->fmt_in.audio);
404     p_filter->fmt_out.audio.i_format = VLC_CODEC_S16N;
405     aout_FormatPrepare(&p_filter->fmt_out.audio);
406
407     return VLC_SUCCESS;
408 }
409
410 /*****************************************************************************
411  * CloseFilter
412  *****************************************************************************/
413 static void CloseFilter( filter_t *p_filter )
414 {
415     filter_sys_t *p_sys = p_filter->p_sys;
416
417     free( p_sys->p_atomic_operations );
418     free( p_sys->p_overflow_buffer );
419     free( p_sys );
420 }
421
422 /*****************************************************************************
423  * Convert
424  *****************************************************************************/
425 static block_t *Convert( filter_t *p_filter, block_t *p_block )
426 {
427     block_t *p_out;
428     int i_out_size;
429
430     if( !p_block || !p_block->i_nb_samples )
431     {
432         if( p_block )
433             block_Release( p_block );
434         return NULL;
435     }
436
437     filter_sys_t *p_sys = p_filter->p_sys;
438     i_out_size = p_block->i_nb_samples * p_sys->i_bitspersample/8 *
439                  aout_FormatNbChannels( &(p_filter->fmt_out.audio) );
440
441     p_out = block_Alloc( i_out_size );
442     if( !p_out )
443     {
444         msg_Warn( p_filter, "can't get output buffer" );
445         block_Release( p_block );
446         return NULL;
447     }
448     p_out->i_nb_samples =
449                   (p_block->i_nb_samples / p_sys->i_nb_channels) *
450                        aout_FormatNbChannels( &(p_filter->fmt_out.audio) );
451
452 #if 0
453     unsigned int i_in_size = in_buf.i_nb_samples  * (p_sys->i_bitspersample/8) *
454                              aout_FormatNbChannels( &(p_filter->fmt_in.audio) );
455     if( (in_buf.i_buffer != i_in_size) && ((i_in_size % 32) != 0) ) /* is it word aligned?? */
456     {
457         msg_Err( p_filter, "input buffer is not word aligned" );
458         /* Fix output buffer to be word aligned */
459     }
460 #endif
461
462     memset( p_out->p_buffer, 0, i_out_size );
463     if( p_sys->b_downmix )
464     {
465         stereo2mono_downmix( p_filter, p_block, p_out );
466         mono( p_filter, p_out, p_block );
467     }
468     else
469     {
470         stereo_to_mono( p_filter, p_out, p_block );
471     }
472
473     block_Release( p_block );
474     return p_out;
475 }
476
477 /* stereo2mono_downmix - stereo channels into one mono channel.
478  * Code taken from modules/audio_filter/channel_mixer/headphone.c
479  * converted from float into int16_t based downmix
480  * Written by Boris Dorès <babal@via.ecp.fr>
481  */
482 static void stereo2mono_downmix( filter_t * p_filter,
483                                  block_t * p_in_buf, block_t * p_out_buf )
484 {
485     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
486
487     int i_input_nb = aout_FormatNbChannels( &p_filter->fmt_in.audio );
488     int i_output_nb = aout_FormatNbChannels( &p_filter->fmt_out.audio );
489
490     int16_t * p_in = (int16_t*) p_in_buf->p_buffer;
491     uint8_t * p_out;
492     uint8_t * p_overflow;
493     uint8_t * p_slide;
494
495     size_t i_overflow_size;     /* in bytes */
496     size_t i_out_size;          /* in bytes */
497
498     unsigned int i, j;
499
500     int i_source_channel_offset;
501     int i_dest_channel_offset;
502     unsigned int i_delay;
503     double d_amplitude_factor;
504
505     /* out buffer characterisitcs */
506     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
507     p_out_buf->i_buffer = p_in_buf->i_buffer * i_output_nb / i_input_nb;
508     p_out = p_out_buf->p_buffer;
509     i_out_size = p_out_buf->i_buffer;
510
511     /* Slide the overflow buffer */
512     p_overflow = p_sys->p_overflow_buffer;
513     i_overflow_size = p_sys->i_overflow_buffer_size;
514
515     if ( i_out_size > i_overflow_size )
516         memcpy( p_out, p_overflow, i_overflow_size );
517     else
518         memcpy( p_out, p_overflow, i_out_size );
519
520     p_slide = p_sys->p_overflow_buffer;
521     while( p_slide < p_overflow + i_overflow_size )
522     {
523         if( p_slide + i_out_size < p_overflow + i_overflow_size )
524         {
525             memset( p_slide, 0, i_out_size );
526             if( p_slide + 2 * i_out_size < p_overflow + i_overflow_size )
527                 memcpy( p_slide, p_slide + i_out_size, i_out_size );
528             else
529                 memcpy( p_slide, p_slide + i_out_size,
530                         p_overflow + i_overflow_size - ( p_slide + i_out_size ) );
531         }
532         else
533         {
534             memset( p_slide, 0, p_overflow + i_overflow_size - p_slide );
535         }
536         p_slide += i_out_size;
537     }
538
539     /* apply the atomic operations */
540     for( i = 0; i < p_sys->i_nb_atomic_operations; i++ )
541     {
542         /* shorter variable names */
543         i_source_channel_offset
544             = p_sys->p_atomic_operations[i].i_source_channel_offset;
545         i_dest_channel_offset
546             = p_sys->p_atomic_operations[i].i_dest_channel_offset;
547         i_delay = p_sys->p_atomic_operations[i].i_delay;
548         d_amplitude_factor
549             = p_sys->p_atomic_operations[i].d_amplitude_factor;
550
551         if( p_out_buf->i_nb_samples > i_delay )
552         {
553             /* current buffer coefficients */
554             for( j = 0; j < p_out_buf->i_nb_samples - i_delay; j++ )
555             {
556                 ((int16_t*)p_out)[ (i_delay+j)*i_output_nb + i_dest_channel_offset ]
557                     += p_in[ j * i_input_nb + i_source_channel_offset ]
558                        * d_amplitude_factor;
559             }
560
561             /* overflow buffer coefficients */
562             for( j = 0; j < i_delay; j++ )
563             {
564                 ((int16_t*)p_overflow)[ j*i_output_nb + i_dest_channel_offset ]
565                     += p_in[ (p_out_buf->i_nb_samples - i_delay + j)
566                        * i_input_nb + i_source_channel_offset ]
567                        * d_amplitude_factor;
568             }
569         }
570         else
571         {
572             /* overflow buffer coefficients only */
573             for( j = 0; j < p_out_buf->i_nb_samples; j++ )
574             {
575                 ((int16_t*)p_overflow)[ (i_delay - p_out_buf->i_nb_samples + j)
576                                         * i_output_nb + i_dest_channel_offset ]
577                     += p_in[ j * i_input_nb + i_source_channel_offset ]
578                        * d_amplitude_factor;
579             }
580         }
581     }
582 }
583
584 /* Simple stereo to mono mixing. */
585 static unsigned int mono( filter_t *p_filter,
586                           block_t *p_output, block_t *p_input )
587 {
588     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
589     int16_t *p_in, *p_out;
590     unsigned int n = 0, r = 0;
591
592     p_in = (int16_t *) p_input->p_buffer;
593     p_out = (int16_t *) p_output->p_buffer;
594
595     while( n < (p_input->i_nb_samples * p_sys->i_nb_channels) )
596     {
597         p_out[r] = (p_in[n] + p_in[n+1]) >> 1;
598         r++;
599         n += 2;
600     }
601     return r;
602 }
603
604 /* Simple stereo to mono mixing. */
605 static unsigned int stereo_to_mono( filter_t *p_filter,
606                                     block_t *p_output, block_t *p_input )
607 {
608     filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
609     int16_t *p_in, *p_out;
610     unsigned int n;
611
612     p_in = (int16_t *) p_input->p_buffer;
613     p_out = (int16_t *) p_output->p_buffer;
614
615     for( n = 0; n < (p_input->i_nb_samples * p_sys->i_nb_channels); n++ )
616     {
617         /* Fake real mono. */
618         if( p_sys->i_channel_selected == -1)
619         {
620             p_out[n] = p_out[n+1] = (p_in[n] + p_in[n+1]) >> 1;
621             n++;
622         }
623         else if( (n % p_sys->i_nb_channels) == (unsigned int) p_sys->i_channel_selected )
624         {
625             p_out[n] = p_out[n+1] = p_in[n];
626         }
627     }
628     return n;
629 }