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