mono.c 24.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*****************************************************************************
 * mono.c : stereo2mono downmixsimple channel mixer plug-in
 *****************************************************************************
 * Copyright (C) 2006 M2X
 * $Id$
 *
 * Authors: Jean-Paul Saman <jpsaman at m2x dot nl>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <math.h>                                        /* sqrt */
32
#include <stdint.h>                                         /* int16_t .. */
33
34
35
36
37

#ifdef HAVE_UNISTD_H
#   include <unistd.h>
#endif

38
#include <vlc_common.h>
39
#include <vlc_plugin.h>
40
41
42
#include <vlc_es.h>
#include <vlc_block.h>
#include <vlc_filter.h>
zorglub's avatar
zorglub committed
43
#include <vlc_aout.h>
44
45
46
47
48
49
50
51
52

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  OpenFilter    ( vlc_object_t * );
static void CloseFilter   ( vlc_object_t * );

static block_t *Convert( filter_t *p_filter, block_t *p_block );

Rafaël Carré's avatar
Rafaël Carré committed
53
54
55
56
57
static unsigned int stereo_to_mono( aout_filter_t *, aout_buffer_t *,
                                    aout_buffer_t * );
static unsigned int mono( aout_filter_t *, aout_buffer_t *, aout_buffer_t * );
static void stereo2mono_downmix( aout_filter_t *, aout_buffer_t *,
                                 aout_buffer_t * );
58
59
60
61

/*****************************************************************************
 * Local structures
 *****************************************************************************/
62
63
64
65
66
67
68
69
struct atomic_operation_t
{
    int i_source_channel_offset;
    int i_dest_channel_offset;
    unsigned int i_delay;/* in sample unit */
    double d_amplitude_factor;
};

70
71
struct filter_sys_t
{
72
    bool b_downmix;
73

74
    unsigned int i_nb_channels; /* number of int16_t per sample */
75
    int i_channel_selected;
76
    int i_bitspersample;
77
78

    size_t i_overflow_buffer_size;/* in bytes */
79
    uint8_t * p_overflow_buffer;
80
81
    unsigned int i_nb_atomic_operations;
    struct atomic_operation_t * p_atomic_operations;
82
83
};

Christophe Mutricy's avatar
Typo    
Christophe Mutricy committed
84
#define MONO_DOWNMIX_TEXT N_("Use downmix algorithm")
85
#define MONO_DOWNMIX_LONGTEXT N_("This option selects a stereo to mono " \
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
86
    "downmix algorithm that is used in the headphone channel mixer. It " \
87
88
    "gives the effect of standing in a room full of speakers." )

89
90
#define MONO_CHANNEL_TEXT N_("Select channel to keep")
#define MONO_CHANNEL_LONGTEXT N_("This option silences all other channels " \
91
    "except the selected channel. Choose one from (0=left, 1=right, " \
Jean-Paul Saman's avatar
Jean-Paul Saman committed
92
    "2=rear left, 3=rear right, 4=center, 5=left front)")
93

94
static const int pi_pos_values[] = { 0, 1, 2, 4, 8, 5 };
95
static const char *const ppsz_pos_descriptions[] =
96
97
98
99
100
101
102
103
{ N_("Left"), N_("Right"), N_("Left rear"), N_("Right rear"), N_("Center"),
  N_("Left front") };

/* our internal channel order (WG-4 order) */
static const uint32_t pi_channels_out[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
  AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };

Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
104
#define MONO_CFG "sout-mono-"
105
106
107
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
108
109
110
vlc_module_begin ()
    set_description( N_("Audio filter for stereo to mono conversion") )
    set_capability( "audio filter2", 2 )
111

112
    add_bool( MONO_CFG "downmix", true, NULL, MONO_DOWNMIX_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
113
              MONO_DOWNMIX_LONGTEXT, false )
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
114
    add_integer( MONO_CFG "channel", -1, NULL, MONO_CHANNEL_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
115
116
        MONO_CHANNEL_LONGTEXT, false )
        change_integer_list( pi_pos_values, ppsz_pos_descriptions, NULL )
117

118
119
120
121
122
    set_category( CAT_AUDIO )
    set_subcategory( SUBCAT_AUDIO_MISC )
    set_callbacks( OpenFilter, CloseFilter )
    set_shortname( "Mono" )
vlc_module_end ()
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/* Init() and ComputeChannelOperations() -
 * Code taken from modules/audio_filter/channel_mixer/headphone.c
 * converted from float into int16_t based downmix
 * Written by Boris Dorès <babal@via.ecp.fr>
 */

/*****************************************************************************
 * Init: initialize internal data structures
 * and computes the needed atomic operations
 *****************************************************************************/
/* x and z represent the coordinates of the virtual speaker
 *  relatively to the center of the listener's head, measured in meters :
 *
 *  left              right
 *Z
 *-
 *a          head
 *x
 *i
 *s
 *  rear left    rear right
 *
 *          x-axis
 *  */
static void ComputeChannelOperations( struct filter_sys_t * p_data,
        unsigned int i_rate, unsigned int i_next_atomic_operation,
        int i_source_channel_offset, double d_x, double d_z,
        double d_compensation_length, double d_channel_amplitude_factor )
{
    double d_c = 340; /*sound celerity (unit: m/s)*/
    double d_compensation_delay = (d_compensation_length-0.1) / d_c * i_rate;

    /* Left ear */
    p_data->p_atomic_operations[i_next_atomic_operation]
        .i_source_channel_offset = i_source_channel_offset;
    p_data->p_atomic_operations[i_next_atomic_operation]
        .i_dest_channel_offset = 0;/* left */
    p_data->p_atomic_operations[i_next_atomic_operation]
        .i_delay = (int)( sqrt( (-0.1-d_x)*(-0.1-d_x) + (0-d_z)*(0-d_z) )
                          / d_c * i_rate - d_compensation_delay );
    if( d_x < 0 )
    {
        p_data->p_atomic_operations[i_next_atomic_operation]
            .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
    }
    else if( d_x > 0 )
    {
        p_data->p_atomic_operations[i_next_atomic_operation]
            .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
    }
    else
    {
        p_data->p_atomic_operations[i_next_atomic_operation]
            .d_amplitude_factor = d_channel_amplitude_factor / 2;
    }

    /* Right ear */
    p_data->p_atomic_operations[i_next_atomic_operation + 1]
        .i_source_channel_offset = i_source_channel_offset;
    p_data->p_atomic_operations[i_next_atomic_operation + 1]
        .i_dest_channel_offset = 1;/* right */
    p_data->p_atomic_operations[i_next_atomic_operation + 1]
        .i_delay = (int)( sqrt( (0.1-d_x)*(0.1-d_x) + (0-d_z)*(0-d_z) )
                          / d_c * i_rate - d_compensation_delay );
    if( d_x < 0 )
    {
        p_data->p_atomic_operations[i_next_atomic_operation + 1]
            .d_amplitude_factor = d_channel_amplitude_factor * 0.9 / 2;
    }
    else if( d_x > 0 )
    {
        p_data->p_atomic_operations[i_next_atomic_operation + 1]
            .d_amplitude_factor = d_channel_amplitude_factor * 1.1 / 2;
    }
    else
    {
        p_data->p_atomic_operations[i_next_atomic_operation + 1]
            .d_amplitude_factor = d_channel_amplitude_factor / 2;
    }
}

static int Init( vlc_object_t *p_this, struct filter_sys_t * p_data,
                 unsigned int i_nb_channels, uint32_t i_physical_channels,
                 unsigned int i_rate )
{
    double d_x = config_GetInt( p_this, "headphone-dim" );
    double d_z = d_x;
    double d_z_rear = -d_x/3;
    double d_min = 0;
    unsigned int i_next_atomic_operation;
    int i_source_channel_offset;
    unsigned int i;

    if( config_GetInt( p_this, "headphone-compensate" ) )
    {
        /* minimal distance to any speaker */
        if( i_physical_channels & AOUT_CHAN_REARCENTER )
        {
            d_min = d_z_rear;
        }
        else
        {
            d_min = d_z;
        }
    }

    /* Number of elementary operations */
    p_data->i_nb_atomic_operations = i_nb_channels * 2;
    if( i_physical_channels & AOUT_CHAN_CENTER )
    {
        p_data->i_nb_atomic_operations += 2;
    }
    p_data->p_atomic_operations = malloc( sizeof(struct atomic_operation_t)
            * p_data->i_nb_atomic_operations );
    if( p_data->p_atomic_operations == NULL )
        return -1;

    /* For each virtual speaker, computes elementary wave propagation time
     * to each ear */
    i_next_atomic_operation = 0;
    i_source_channel_offset = 0;
    if( i_physical_channels & AOUT_CHAN_LEFT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , -d_x , d_z , d_min , 2.0 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_RIGHT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , d_x , d_z , d_min , 2.0 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_MIDDLELEFT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , -d_x , 0 , d_min , 1.5 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_MIDDLERIGHT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , d_x , 0 , d_min , 1.5 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_REARLEFT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , -d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_REARRIGHT )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , d_x , d_z_rear , d_min , 1.5 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_REARCENTER )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , 0 , -d_z , d_min , 1.5 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_CENTER )
    {
        /* having two center channels increases the spatialization effect */
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
        i_next_atomic_operation += 2;
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , -d_x / 5.0 , d_z , d_min , 0.75 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }
    if( i_physical_channels & AOUT_CHAN_LFE )
    {
        ComputeChannelOperations( p_data , i_rate
                , i_next_atomic_operation , i_source_channel_offset
                , 0 , d_z_rear , d_min , 5.0 / i_nb_channels );
        i_next_atomic_operation += 2;
        i_source_channel_offset++;
    }

    /* Initialize the overflow buffer
     * we need it because the process induce a delay in the samples */
    p_data->i_overflow_buffer_size = 0;
    for( i = 0 ; i < p_data->i_nb_atomic_operations ; i++ )
    {
        if( p_data->i_overflow_buffer_size
                < p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t) )
        {
            p_data->i_overflow_buffer_size
                = p_data->p_atomic_operations[i].i_delay * 2 * sizeof (int16_t);
        }
    }
    p_data->p_overflow_buffer = malloc( p_data->i_overflow_buffer_size );
336
337
338
    if( p_data->p_overflow_buffer == NULL )
    {
        free( p_data->p_atomic_operations );
339
        return -1;
340
    }
341
    memset( p_data->p_overflow_buffer, 0, p_data->i_overflow_buffer_size );
342
343
344
345
346

    /* end */
    return 0;
}

347
348
349
350
351
352
353
354
355
356
/*****************************************************************************
 * OpenFilter
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t * p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys = NULL;

    if( aout_FormatNbChannels( &(p_filter->fmt_in.audio) ) == 1 )
    {
357
        /*msg_Dbg( p_filter, "filter discarded (incompatible format)" );*/
358
359
360
361
362
363
        return VLC_EGENERIC;
    }

    if( (p_filter->fmt_in.i_codec != AOUT_FMT_S16_NE) ||
        (p_filter->fmt_out.i_codec != AOUT_FMT_S16_NE) )
    {
364
365
        /*msg_Err( p_this, "filter discarded (invalid format)" );*/
        return VLC_EGENERIC;
366
367
368
369
370
    }

    if( (p_filter->fmt_in.audio.i_format != p_filter->fmt_out.audio.i_format) &&
        (p_filter->fmt_in.audio.i_rate != p_filter->fmt_out.audio.i_rate) &&
        (p_filter->fmt_in.audio.i_format != AOUT_FMT_S16_NE) &&
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
371
372
373
        (p_filter->fmt_out.audio.i_format != AOUT_FMT_S16_NE) &&
        (p_filter->fmt_in.audio.i_bitspersample !=
                                    p_filter->fmt_out.audio.i_bitspersample))
374
    {
375
        /*msg_Err( p_this, "couldn't load mono filter" );*/
376
377
378
379
380
381
382
383
        return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store the module's structure */
    p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
    if( p_sys == NULL )
        return VLC_EGENERIC;

Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
384
    var_Create( p_this, MONO_CFG "downmix",
385
                VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
386
    p_sys->b_downmix = var_GetBool( p_this, MONO_CFG "downmix" );
387

Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
388
    var_Create( p_this, MONO_CFG "channel",
389
390
                VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
    p_sys->i_channel_selected =
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
391
            (unsigned int) var_GetInteger( p_this, MONO_CFG "channel" );
392

393
394
    if( p_sys->b_downmix )
    {
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
395
        msg_Dbg( p_this, "using stereo to mono downmix" );
396
        p_filter->fmt_out.audio.i_physical_channels = AOUT_CHAN_CENTER;
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
397
        p_filter->fmt_out.audio.i_channels = 1;
398
399
400
    }
    else
    {
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
401
        msg_Dbg( p_this, "using pseudo mono" );
402
        p_filter->fmt_out.audio.i_physical_channels =
403
                            (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT);
Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
404
        p_filter->fmt_out.audio.i_channels = 2;
405
406
    }

407
408
409
    p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
    p_filter->fmt_out.audio.i_format = p_filter->fmt_out.i_codec;

410
    p_sys->i_nb_channels = aout_FormatNbChannels( &(p_filter->fmt_in.audio) );
411
412
    p_sys->i_bitspersample = p_filter->fmt_out.audio.i_bitspersample;

413
414
415
416
417
418
419
420
421
422
    p_sys->i_overflow_buffer_size = 0;
    p_sys->p_overflow_buffer = NULL;
    p_sys->i_nb_atomic_operations = 0;
    p_sys->p_atomic_operations = NULL;

    if( Init( VLC_OBJECT(p_filter), p_filter->p_sys,
              aout_FormatNbChannels( &p_filter->fmt_in.audio ),
              p_filter->fmt_in.audio.i_physical_channels,
              p_filter->fmt_in.audio.i_rate ) < 0 )
    {
423
424
425
        var_Destroy( p_this, MONO_CFG "channel" );
        var_Destroy( p_this, MONO_CFG "downmix" );
        free( p_sys );
426
427
428
        return VLC_EGENERIC;
    }

429
430
    p_filter->pf_audio_filter = Convert;

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    msg_Dbg( p_this, "%4.4s->%4.4s, channels %d->%d, bits per sample: %i->%i",
             (char *)&p_filter->fmt_in.i_codec,
             (char *)&p_filter->fmt_out.i_codec,
             p_filter->fmt_in.audio.i_physical_channels,
             p_filter->fmt_out.audio.i_physical_channels,
             p_filter->fmt_in.audio.i_bitspersample,
             p_filter->fmt_out.audio.i_bitspersample );

    return VLC_SUCCESS;
}

/*****************************************************************************
 * CloseFilter
 *****************************************************************************/
static void CloseFilter( vlc_object_t *p_this)
{
    filter_t *p_filter = (filter_t *) p_this;
    filter_sys_t *p_sys = p_filter->p_sys;

Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
450
451
    var_Destroy( p_this, MONO_CFG "channel" );
    var_Destroy( p_this, MONO_CFG "downmix" );
452
453
    free( p_sys->p_atomic_operations );
    free( p_sys->p_overflow_buffer );
454
455
456
457
458
459
460
461
462
463
464
    free( p_sys );
}

/*****************************************************************************
 * Convert
 *****************************************************************************/
static block_t *Convert( filter_t *p_filter, block_t *p_block )
{
    aout_filter_t aout_filter;
    aout_buffer_t in_buf, out_buf;
    block_t *p_out = NULL;
465
    unsigned int i_samples;
466
    int i_out_size;
467
468
469
470

    if( !p_block || !p_block->i_samples )
    {
        if( p_block )
471
            block_Release( p_block );
472
473
474
475
        return NULL;
    }

    i_out_size = p_block->i_samples * p_filter->p_sys->i_bitspersample/8 *
476
                 aout_FormatNbChannels( &(p_filter->fmt_out.audio) );
477
478
479
480
481

    p_out = p_filter->pf_audio_buffer_new( p_filter, i_out_size );
    if( !p_out )
    {
        msg_Warn( p_filter, "can't get output buffer" );
482
        block_Release( p_block );
483
484
        return NULL;
    }
485
    p_out->i_samples = (p_block->i_samples / p_filter->p_sys->i_nb_channels) *
486
                       aout_FormatNbChannels( &(p_filter->fmt_out.audio) );
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
    p_out->i_dts = p_block->i_dts;
    p_out->i_pts = p_block->i_pts;
    p_out->i_length = p_block->i_length;

    aout_filter.p_sys = (struct aout_filter_sys_t *)p_filter->p_sys;
    aout_filter.input = p_filter->fmt_in.audio;
    aout_filter.input.i_format = p_filter->fmt_in.i_codec;
    aout_filter.output = p_filter->fmt_out.audio;
    aout_filter.output.i_format = p_filter->fmt_out.i_codec;

    in_buf.p_buffer = p_block->p_buffer;
    in_buf.i_nb_bytes = p_block->i_buffer;
    in_buf.i_nb_samples = p_block->i_samples;

#if 0
502
503
504
    unsigned int i_in_size = in_buf.i_nb_samples  * (p_filter->p_sys->i_bitspersample/8) *
                             aout_FormatNbChannels( &(p_filter->fmt_in.audio) );
    if( (in_buf.i_nb_bytes != i_in_size) && ((i_in_size % 32) != 0) ) /* is it word aligned?? */
505
    {
506
507
        msg_Err( p_filter, "input buffer is not word aligned" );
        /* Fix output buffer to be word aligned */
508
509
510
511
512
513
514
    }
#endif

    out_buf.p_buffer = p_out->p_buffer;
    out_buf.i_nb_bytes = p_out->i_buffer;
    out_buf.i_nb_samples = p_out->i_samples;

Jean-Paul Saman's avatar
Cleanup    
Jean-Paul Saman committed
515
    memset( p_out->p_buffer, 0, i_out_size );
516
517
    if( p_filter->p_sys->b_downmix )
    {
Rafaël Carré's avatar
Rafaël Carré committed
518
519
        stereo2mono_downmix( &aout_filter, &in_buf, &out_buf );
        i_samples = mono( &aout_filter, &out_buf, &in_buf );
520
521
522
    }
    else
    {
Rafaël Carré's avatar
Rafaël Carré committed
523
        i_samples = stereo_to_mono( &aout_filter, &out_buf, &in_buf );
524
    }
525
526
527
528

    p_out->i_buffer = out_buf.i_nb_bytes;
    p_out->i_samples = out_buf.i_nb_samples;

529
    block_Release( p_block );
530
531
532
    return p_out;
}

533
534
535
536
/* stereo2mono_downmix - stereo channels into one mono channel.
 * Code taken from modules/audio_filter/channel_mixer/headphone.c
 * converted from float into int16_t based downmix
 * Written by Boris Dorès <babal@via.ecp.fr>
537
 */
Rafaël Carré's avatar
Rafaël Carré committed
538
static void stereo2mono_downmix( aout_filter_t * p_filter,
539
540
541
542
543
544
545
546
                            aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;

    int i_input_nb = aout_FormatNbChannels( &p_filter->input );
    int i_output_nb = aout_FormatNbChannels( &p_filter->output );

    int16_t * p_in = (int16_t*) p_in_buf->p_buffer;
547
548
549
    uint8_t * p_out;
    uint8_t * p_overflow;
    uint8_t * p_slide;
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646

    size_t i_overflow_size;     /* in bytes */
    size_t i_out_size;          /* in bytes */

    unsigned int i, j;

    int i_source_channel_offset;
    int i_dest_channel_offset;
    unsigned int i_delay;
    double d_amplitude_factor;

    /* out buffer characterisitcs */
    p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
    p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes * i_output_nb / i_input_nb;
    p_out = p_out_buf->p_buffer;
    i_out_size = p_out_buf->i_nb_bytes;

    if( p_sys != NULL )
    {
        /* Slide the overflow buffer */
        p_overflow = p_sys->p_overflow_buffer;
        i_overflow_size = p_sys->i_overflow_buffer_size;

        if ( i_out_size > i_overflow_size )
            memcpy( p_out, p_overflow, i_overflow_size );
        else
            memcpy( p_out, p_overflow, i_out_size );

        p_slide = p_sys->p_overflow_buffer;
        while( p_slide < p_overflow + i_overflow_size )
        {
            if( p_slide + i_out_size < p_overflow + i_overflow_size )
            {
                memset( p_slide, 0, i_out_size );
                if( p_slide + 2 * i_out_size < p_overflow + i_overflow_size )
                    memcpy( p_slide, p_slide + i_out_size, i_out_size );
                else
                    memcpy( p_slide, p_slide + i_out_size,
                            p_overflow + i_overflow_size - ( p_slide + i_out_size ) );
            }
            else
            {
                memset( p_slide, 0, p_overflow + i_overflow_size - p_slide );
            }
            p_slide += i_out_size;
        }

        /* apply the atomic operations */
        for( i = 0; i < p_sys->i_nb_atomic_operations; i++ )
        {
            /* shorter variable names */
            i_source_channel_offset
                = p_sys->p_atomic_operations[i].i_source_channel_offset;
            i_dest_channel_offset
                = p_sys->p_atomic_operations[i].i_dest_channel_offset;
            i_delay = p_sys->p_atomic_operations[i].i_delay;
            d_amplitude_factor
                = p_sys->p_atomic_operations[i].d_amplitude_factor;

            if( p_out_buf->i_nb_samples > i_delay )
            {
                /* current buffer coefficients */
                for( j = 0; j < p_out_buf->i_nb_samples - i_delay; j++ )
                {
                    ((int16_t*)p_out)[ (i_delay+j)*i_output_nb + i_dest_channel_offset ]
                        += p_in[ j * i_input_nb + i_source_channel_offset ]
                           * d_amplitude_factor;
                }

                /* overflow buffer coefficients */
                for( j = 0; j < i_delay; j++ )
                {
                    ((int16_t*)p_overflow)[ j*i_output_nb + i_dest_channel_offset ]
                        += p_in[ (p_out_buf->i_nb_samples - i_delay + j)
                           * i_input_nb + i_source_channel_offset ]
                           * d_amplitude_factor;
                }
            }
            else
            {
                /* overflow buffer coefficients only */
                for( j = 0; j < p_out_buf->i_nb_samples; j++ )
                {
                    ((int16_t*)p_overflow)[ (i_delay - p_out_buf->i_nb_samples + j)
                        * i_output_nb + i_dest_channel_offset ]
                        += p_in[ j * i_input_nb + i_source_channel_offset ]
                           * d_amplitude_factor;
                }
            }
        }
    }
    else
    {
        memset( p_out, 0, i_out_size );
    }
}

647
/* Simple stereo to mono mixing. */
Rafaël Carré's avatar
Rafaël Carré committed
648
static unsigned int mono( aout_filter_t *p_filter,
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
                          aout_buffer_t *p_output, aout_buffer_t *p_input )
{
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
    int16_t *p_in, *p_out;
    unsigned int n = 0, r = 0;

    p_in = (int16_t *) p_input->p_buffer;
    p_out = (int16_t *) p_output->p_buffer;

    while( n < (p_input->i_nb_samples * p_sys->i_nb_channels) )
    {
        p_out[r] = (p_in[n] + p_in[n+1]) >> 1;
        r++;
        n += 2;
    }
    return r;
}

667
/* Simple stereo to mono mixing. */
Rafaël Carré's avatar
Rafaël Carré committed
668
static unsigned int stereo_to_mono( aout_filter_t *p_filter,
669
                                    aout_buffer_t *p_output, aout_buffer_t *p_input )
670
671
672
{
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
    int16_t *p_in, *p_out;
673
    unsigned int n;
674

675
676
    p_in = (int16_t *) p_input->p_buffer;
    p_out = (int16_t *) p_output->p_buffer;
677

678
    for( n = 0; n < (p_input->i_nb_samples * p_sys->i_nb_channels); n++ )
679
    {
680
        /* Fake real mono. */
681
682
683
684
685
        if( p_sys->i_channel_selected == -1)
        {
            p_out[n] = p_out[n+1] = (p_in[n] + p_in[n+1]) >> 1;
            n++;
        }
686
        else if( (n % p_sys->i_nb_channels) == (unsigned int) p_sys->i_channel_selected )
687
688
689
        {
            p_out[n] = p_out[n+1] = p_in[n];
        }
690
691
692
    }
    return n;
}