normvol.c 8.35 KB
Newer Older
1
2
3
/*****************************************************************************
 * normvol.c :  volume normalizer
 *****************************************************************************
4
 * Copyright (C) 2001 the VideoLAN team
5
 * $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 *
 * Authors: Clment Stenac <zorglub@videolan.org>
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*
 * TODO:
 *
 * We should detect fast power increases and react faster to these
 * This way, we can increase the buffer size to get a more stable filter */


/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>

#include <errno.h>                                                 /* ENOMEM */
#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#include <math.h>


#include <vlc/vlc.h>
#include <vlc/aout.h>

#include <aout_internal.h>

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/

static int  Open     ( vlc_object_t * );
static void Close    ( vlc_object_t * );
static void DoWork   ( aout_instance_t * , aout_filter_t *,
                aout_buffer_t * , aout_buffer_t *);

59
typedef struct aout_filter_sys_t
60
{
61
62
63
    int i_nb;
    float *p_last;
    float f_max;
64
} aout_filter_sys_t;
65
66
67
68
69
70
71
72

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define BUFF_TEXT N_("Number of audio buffers" )
#define BUFF_LONGTEXT N_("This is the number of audio buffers on which the " \
                "power measurement is made. A higher number of buffers will " \
                "increase the response time of the filter to a high " \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
73
                "power but will make it less sensitive to short variations." )
74
75
76

#define LEVEL_TEXT N_("Max level" )
#define LEVEL_LONGTEXT N_("If the average power over the last N buffers " \
zorglub's avatar
zorglub committed
77
               "is higher than this value, the volume will be normalized. " \
78
79
80
81
82
               "This value is a positive floating point number. A value " \
               "between 0.5 and 10 seems sensible." )

vlc_module_begin();
    set_description( _("Volume normalizer") );
zorglub's avatar
   
zorglub committed
83
    set_shortname( N_("Volume normalizer") );
zorglub's avatar
zorglub committed
84
85
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_AFILTER );
86
87
88
89
90
    add_shortcut( "volnorm" );
    add_integer( "norm-buff-size", 20 ,NULL ,BUFF_TEXT, BUFF_LONGTEXT,
                 VLC_TRUE);
    add_float( "norm-max-level", 2.0, NULL, LEVEL_TEXT,
               LEVEL_LONGTEXT, VLC_TRUE );
91
92
93
94
95
96
97
98
99
100
    set_capability( "audio filter", 0 );
    set_callbacks( Open, Close );
vlc_module_end();

/*****************************************************************************
 * Open: initialize and create stuff
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    aout_filter_t *p_filter = (aout_filter_t*)p_this;
101
    vlc_bool_t b_fit = VLC_TRUE;
102
103
104
    int i_channels;
    aout_filter_sys_t *p_sys = p_filter->p_sys =
        malloc( sizeof( aout_filter_sys_t ) );
105
106
107
108

    if( p_filter->input.i_format != VLC_FOURCC('f','l','3','2' ) ||
        p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
    {
109
110
111
112
        b_fit = VLC_FALSE;
        p_filter->input.i_format = VLC_FOURCC('f','l','3','2');
        p_filter->output.i_format = VLC_FOURCC('f','l','3','2');
        msg_Warn( p_filter, "Bad input or output format" );
113
114
115
116
    }

    if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
    {
117
118
119
        b_fit = VLC_FALSE;
        memcpy( &p_filter->output, &p_filter->input,
                sizeof(audio_sample_format_t) );
120
        msg_Warn( p_filter, "input and output formats are not similar" );
121
122
123
124
    }

    if ( ! b_fit )
    {
125
126
127
128
129
130
        return VLC_EGENERIC;
    }

    p_filter->pf_do_work = DoWork;
    p_filter->b_in_place = VLC_TRUE;

131
    i_channels = aout_FormatNbChannels( &p_filter->input );
132

133
    p_sys->i_nb = var_CreateGetInteger( p_filter->p_parent, "norm-buff-size" );
134
135
    p_sys->f_max = var_CreateGetFloat( p_filter->p_parent, "norm-max-level" );

136
    if( p_sys->f_max <= 0 ) p_sys->f_max = 0.01;
137
138
139
140

    /* We need to store (nb_buffers+1)*nb_channels floats */
    p_sys->p_last = malloc( sizeof( float ) * (i_channels) *
                            (p_filter->p_sys->i_nb + 2) );
141
142
    memset( p_sys->p_last, 0 ,sizeof( float ) * (i_channels) *
            (p_filter->p_sys->i_nb + 2) );
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
    return VLC_SUCCESS;
}

/*****************************************************************************
 * DoWork : normalizes and sends a buffer
 *****************************************************************************/
 static void DoWork( aout_instance_t *p_aout, aout_filter_t *p_filter,
                     aout_buffer_t *p_in_buf, aout_buffer_t *p_out_buf )
{
    float *pf_sum;
    float *pf_gain;
    float f_average = 0;
    int i, i_chan;

    int i_samples = p_in_buf->i_nb_samples;
    int i_channels = aout_FormatNbChannels( &p_filter->input );
    float *p_out = (float*)p_out_buf->p_buffer;
    float *p_in =  (float*)p_in_buf->p_buffer;

    struct aout_filter_sys_t *p_sys = p_filter->p_sys;

    pf_sum = (float *)malloc( sizeof(float) * i_channels );
    memset( pf_sum, 0, sizeof(float) * i_channels );

    pf_gain = (float *)malloc( sizeof(float) * i_channels );

    p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
    p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;

172
173
174
    /* Calculate the average power level on this buffer */
    for( i = 0 ; i < i_samples; i++ )
    {
175
176
177
178
179
180
181
        for( i_chan = 0; i_chan < i_channels; i_chan++ )
        {
            float f_sample = p_in[i_chan];
            float f_square = pow( f_sample, 2 );
            pf_sum[i_chan] += f_square;
        }
        p_in += i_channels;
182
    }
183

184
185
186
    /* sum now contains for each channel the sigma(value) */
    for( i_chan = 0; i_chan < i_channels; i_chan++ )
    {
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
        /* Shift our lastbuff */
        memmove( &p_sys->p_last[ i_chan * p_sys->i_nb],
                        &p_sys->p_last[i_chan * p_sys->i_nb + 1],
                 (p_sys->i_nb-1) * sizeof( float ) );

        /* Insert the new average : sqrt(sigma(value)) */
        p_sys->p_last[ i_chan * p_sys->i_nb + p_sys->i_nb - 1] =
                sqrt( pf_sum[i_chan] );

        pf_sum[i_chan] = 0;

        /* Get the average power on the lastbuff */
        f_average = 0;
        for( i = 0; i < p_sys->i_nb ; i++)
        {
            f_average += p_sys->p_last[ i_chan * p_sys->i_nb + i ];
        }
        f_average = f_average / p_sys->i_nb;

        /* Seuil arbitraire */
        p_sys->f_max = var_GetFloat( p_aout, "norm-max-level" );

209
        //fprintf(stderr,"Average %f, max %f\n", f_average, p_sys->f_max );
210
211
212
213
214
215
216
217
218
219
220
        if( f_average > p_sys->f_max )
        {
             pf_gain[i_chan] = f_average / p_sys->f_max;
        }
        else
        {
           pf_gain[i_chan] = 1;
        }
    }

    /* Apply gain */
221
    for( i = 0; i < i_samples; i++)
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
    {
        for( i_chan = 0; i_chan < i_channels; i_chan++ )
        {
            p_out[i_chan] /= pf_gain[i_chan];
        }
        p_out += i_channels;
    }

    free( pf_sum );
    free( pf_gain );

    return;
}

/**********************************************************************
 * Close
 **********************************************************************/
static void Close( vlc_object_t *p_this )
{
    aout_filter_t *p_filter = (aout_filter_t*)p_this;
242
    aout_filter_sys_t *p_sys = p_filter->p_sys;
243
244
245

    if( p_sys )
    {
246
        if( p_sys->p_last) free( p_sys->p_last );
247
248
249
        free( p_sys );
    }
}