]> git.sesse.net Git - mlt/blob - src/modules/normalize/filter_volume.c
Fix calloc() parameter ordering
[mlt] / src / modules / normalize / filter_volume.c
1 /*
2  * filter_volume.c -- adjust audio volume
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include <framework/mlt_filter.h>
22 #include <framework/mlt_frame.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #include <ctype.h>
28 #include <string.h>
29
30 #define MAX_CHANNELS 6
31 #define EPSILON 0.00001
32
33 /* The following normalise functions come from the normalize utility:
34    Copyright (C) 1999--2002 Chris Vaill */
35
36 #define samp_width 16
37
38 #ifndef ROUND
39 # define ROUND(x) floor((x) + 0.5)
40 #endif
41
42 #define DBFSTOAMP(x) pow(10,(x)/20.0)
43
44 /** Return nonzero if the two strings are equal, ignoring case, up to
45     the first n characters.
46 */
47 int strncaseeq(const char *s1, const char *s2, size_t n)
48 {
49         for ( ; n > 0; n--)
50         {
51                 if (tolower(*s1++) != tolower(*s2++))
52                         return 0;
53         }
54         return 1;
55 }
56
57 /** Limiter function.
58  
59          / tanh((x + lev) / (1-lev)) * (1-lev) - lev        (for x < -lev)
60          |
61     x' = | x                                                (for |x| <= lev)
62          |
63          \ tanh((x - lev) / (1-lev)) * (1-lev) + lev        (for x > lev)
64  
65   With limiter level = 0, this is equivalent to a tanh() function;
66   with limiter level = 1, this is equivalent to clipping.
67 */
68 static inline double limiter( double x, double lmtr_lvl )
69 {
70         double xp = x;
71
72         if (x < -lmtr_lvl)
73                 xp = tanh((x + lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) - lmtr_lvl;
74         else if (x > lmtr_lvl)
75                 xp = tanh((x - lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) + lmtr_lvl;
76
77 //      if ( x != xp )
78 //              fprintf( stderr, "filter_volume: sample %f limited %f\n", x, xp );
79
80         return xp;
81 }
82
83
84 /** Takes a full smoothing window, and returns the value of the center
85     element, smoothed.
86
87     Currently, just does a mean filter, but we could do a median or
88     gaussian filter here instead.
89 */
90 static inline double get_smoothed_data( double *buf, int count )
91 {
92         int i, j;
93         double smoothed = 0;
94
95         for ( i = 0, j = 0; i < count; i++ )
96         {
97                 if ( buf[ i ] != -1.0 )
98                 {
99                         smoothed += buf[ i ];
100                         j++;
101                 }
102         }
103         if (j) smoothed /= j;
104 //      fprintf( stderr, "smoothed over %d values, result %f\n", j, smoothed );
105
106         return smoothed;
107 }
108
109 /** Get the max power level (using RMS) and peak level of the audio segment.
110  */
111 double signal_max_power( int16_t *buffer, int channels, int samples, int16_t *peak )
112 {
113         // Determine numeric limits
114         int bytes_per_samp = (samp_width - 1) / 8 + 1;
115         int16_t max = (1 << (bytes_per_samp * 8 - 1)) - 1;
116         int16_t min = -max - 1;
117         
118         double *sums = (double *) calloc( channels, sizeof(double) );
119         int c, i;
120         int16_t sample;
121         double pow, maxpow = 0;
122
123         /* initialize peaks to effectively -inf and +inf */
124         int16_t max_sample = min;
125         int16_t min_sample = max;
126   
127         for ( i = 0; i < samples; i++ )
128         {
129                 for ( c = 0; c < channels; c++ )
130                 {
131                         sample = *buffer++;
132                         sums[ c ] += (double) sample * (double) sample;
133                         
134                         /* track peak */
135                         if ( sample > max_sample )
136                                 max_sample = sample;
137                         else if ( sample < min_sample )
138                                 min_sample = sample;
139                 }
140         }
141         for ( c = 0; c < channels; c++ )
142         {
143                 pow = sums[ c ] / (double) samples;
144                 if ( pow > maxpow )
145                         maxpow = pow;
146         }
147                         
148         free( sums );
149         
150         /* scale the pow value to be in the range 0.0 -- 1.0 */
151         maxpow /= ( (double) min * (double) min);
152
153         if ( -min_sample > max_sample )
154                 *peak = min_sample / (double) min;
155         else
156                 *peak = max_sample / (double) max;
157
158         return sqrt( maxpow );
159 }
160
161 /* ------ End normalize functions --------------------------------------- */
162
163 /** Get the audio.
164 */
165
166 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
167 {
168         // Get the filter from the frame
169         mlt_filter this = mlt_frame_pop_audio( frame );
170
171         // Get the properties from the filter
172         mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
173
174         // Get the frame's filter instance properties
175         mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( this ) );
176
177         // Get the parameters
178         double gain = mlt_properties_get_double( instance_props, "gain" );
179         double max_gain = mlt_properties_get_double( instance_props, "max_gain" );
180         double limiter_level = 0.5; /* -6 dBFS */
181         int normalise =  mlt_properties_get_int( instance_props, "normalise" );
182         double amplitude =  mlt_properties_get_double( instance_props, "amplitude" );
183         int i, j;
184         double sample;
185         int16_t peak;
186
187         if ( mlt_properties_get( instance_props, "limiter" ) != NULL )
188                 limiter_level = mlt_properties_get_double( instance_props, "limiter" );
189         
190         // Get the producer's audio
191         *format = mlt_audio_s16;
192         mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
193 //      fprintf( stderr, "filter_volume: frequency %d\n", *frequency );
194
195         // Determine numeric limits
196         int bytes_per_samp = (samp_width - 1) / 8 + 1;
197         int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1;
198         int samplemin = -samplemax - 1;
199
200         mlt_service_lock( MLT_FILTER_SERVICE( this ) );
201
202         if ( normalise )
203         {
204                 int window = mlt_properties_get_int( filter_props, "window" );
205                 double *smooth_buffer = mlt_properties_get_data( filter_props, "smooth_buffer", NULL );
206
207                 if ( window > 0 && smooth_buffer != NULL )
208                 {
209                         int smooth_index = mlt_properties_get_int( filter_props, "_smooth_index" );
210                         
211                         // Compute the signal power and put into smoothing buffer
212                         smooth_buffer[ smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak );
213 //                      fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ smooth_index ] );
214                         if ( smooth_buffer[ smooth_index ] > EPSILON )
215                         {
216                                 mlt_properties_set_int( filter_props, "_smooth_index", ( smooth_index + 1 ) % window );
217
218                                 // Smooth the data and compute the gain
219 //                              fprintf( stderr, "smoothed %f over %d frames\n", get_smoothed_data( smooth_buffer, window ), window );
220                                 gain *= amplitude / get_smoothed_data( smooth_buffer, window );
221                         }
222                 }
223                 else
224                 {
225                         gain *= amplitude / signal_max_power( (int16_t*) *buffer, *channels, *samples, &peak );
226                 }
227         }
228         
229 //      if ( gain > 1.0 && normalise )
230 //              fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain );
231
232         if ( max_gain > 0 && gain > max_gain )
233                 gain = max_gain;
234
235         // Initialise filter's previous gain value to prevent an inadvertant jump from 0
236         mlt_position last_position = mlt_properties_get_position( filter_props, "_last_position" );
237         mlt_position current_position = mlt_frame_get_position( frame );
238         if ( mlt_properties_get( filter_props, "_previous_gain" ) == NULL
239              || current_position != last_position + 1 )
240                 mlt_properties_set_double( filter_props, "_previous_gain", gain );
241
242         // Start the gain out at the previous
243         double previous_gain = mlt_properties_get_double( filter_props, "_previous_gain" );
244
245         // Determine ramp increment
246         double gain_step = ( gain - previous_gain ) / *samples;
247 //      fprintf( stderr, "filter_volume: previous gain %f current gain %f step %f\n", previous_gain, gain, gain_step );
248
249         // Save the current gain for the next iteration
250         mlt_properties_set_double( filter_props, "_previous_gain", gain );
251         mlt_properties_set_position( filter_props, "_last_position", current_position );
252
253         mlt_service_unlock( MLT_FILTER_SERVICE( this ) );
254
255         // Ramp from the previous gain to the current
256         gain = previous_gain;
257
258         int16_t *p = (int16_t*) *buffer;
259
260         // Apply the gain
261         for ( i = 0; i < *samples; i++ )
262         {
263                 for ( j = 0; j < *channels; j++ )
264                 {
265                         sample = *p * gain;
266                         *p = ROUND( sample );
267                 
268                         if ( gain > 1.0 )
269                         {
270                                 /* use limiter function instead of clipping */
271                                 if ( normalise )
272                                         *p = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) );
273                                 
274                                 /* perform clipping */
275                                 else if ( sample > samplemax )
276                                         *p = samplemax;
277                                 else if ( sample < samplemin )
278                                         *p = samplemin;
279                         }
280                         p++;
281                 }
282                 gain += gain_step;
283         }
284         
285         return 0;
286 }
287
288 /** Filter processing.
289 */
290
291 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
292 {
293         mlt_properties filter_props = MLT_FILTER_PROPERTIES( this );
294         mlt_properties instance_props = mlt_frame_unique_properties( frame, MLT_FILTER_SERVICE( this ) );
295
296         double gain = 1.0; // no adjustment
297
298         // Parse the gain property
299         if ( mlt_properties_get( filter_props, "gain" ) != NULL )
300         {
301                 char *p = mlt_properties_get( filter_props, "gain" );
302
303                 if ( strncaseeq( p, "normalise", 9 ) )
304                         mlt_properties_set( filter_props, "normalise", "" );
305                 else
306                 {
307                         if ( strcmp( p, "" ) != 0 )
308                                 gain = strtod( p, &p );
309
310                         while ( isspace( *p ) )
311                                 p++;
312
313                         /* check if "dB" is given after number */
314                         if ( strncaseeq( p, "db", 2 ) )
315                                 gain = DBFSTOAMP( gain );
316                         else
317                                 gain = fabs( gain );
318
319                         // If there is an end adjust gain to the range
320                         if ( mlt_properties_get( filter_props, "end" ) != NULL )
321                         {
322                                 double end = -1;
323                                 char *p = mlt_properties_get( filter_props, "end" );
324                                 if ( strcmp( p, "" ) != 0 )
325                                         end = strtod( p, &p );
326
327                                 while ( isspace( *p ) )
328                                         p++;
329
330                                 /* check if "dB" is given after number */
331                                 if ( strncaseeq( p, "db", 2 ) )
332                                         end = DBFSTOAMP( gain );
333                                 else
334                                         end = fabs( end );
335
336                                 if ( end != -1 )
337                                         gain += ( end - gain ) * mlt_filter_get_progress( this, frame );
338                         }
339                 }
340         }
341         mlt_properties_set_double( instance_props, "gain", gain );
342         
343         // Parse the maximum gain property
344         if ( mlt_properties_get( filter_props, "max_gain" ) != NULL )
345         {
346                 char *p = mlt_properties_get( filter_props, "max_gain" );
347                 double gain = strtod( p, &p ); // 0 = no max
348                         
349                 while ( isspace( *p ) )
350                         p++;
351
352                 /* check if "dB" is given after number */
353                 if ( strncaseeq( p, "db", 2 ) )
354                         gain = DBFSTOAMP( gain );
355                 else
356                         gain = fabs( gain );
357                         
358                 mlt_properties_set_double( instance_props, "max_gain", gain );
359         }
360
361         // Parse the limiter property
362         if ( mlt_properties_get( filter_props, "limiter" ) != NULL )
363         {
364                 char *p = mlt_properties_get( filter_props, "limiter" );
365                 double level = 0.5; /* -6dBFS */ 
366                 if ( strcmp( p, "" ) != 0 )
367                         level = strtod( p, &p);
368                 
369                 while ( isspace( *p ) )
370                         p++;
371                 
372                 /* check if "dB" is given after number */
373                 if ( strncaseeq( p, "db", 2 ) )
374                 {
375                         if ( level > 0 )
376                                 level = -level;
377                         level = DBFSTOAMP( level );
378                 }
379                 else
380                 {
381                         if ( level < 0 )
382                                 level = -level;
383                 }
384                 mlt_properties_set_double( instance_props, "limiter", level );
385         }
386
387         // Parse the normalise property
388         if ( mlt_properties_get( filter_props, "normalise" ) != NULL )
389         {
390                 char *p = mlt_properties_get( filter_props, "normalise" );
391                 double amplitude = 0.2511886431509580; /* -12dBFS */
392                 if ( strcmp( p, "" ) != 0 )
393                         amplitude = strtod( p, &p);
394
395                 while ( isspace( *p ) )
396                         p++;
397
398                 /* check if "dB" is given after number */
399                 if ( strncaseeq( p, "db", 2 ) )
400                 {
401                         if ( amplitude > 0 )
402                                 amplitude = -amplitude;
403                         amplitude = DBFSTOAMP( amplitude );
404                 }
405                 else
406                 {
407                         if ( amplitude < 0 )
408                                 amplitude = -amplitude;
409                         if ( amplitude > 1.0 )
410                                 amplitude = 1.0;
411                 }
412                 
413                 // If there is an end adjust gain to the range
414                 if ( mlt_properties_get( filter_props, "end" ) != NULL )
415                 {
416                         amplitude *= mlt_filter_get_progress( this, frame );
417                 }
418                 mlt_properties_set_int( instance_props, "normalise", 1 );
419                 mlt_properties_set_double( instance_props, "amplitude", amplitude );
420         }
421
422         // Parse the window property and allocate smoothing buffer if needed
423         int window = mlt_properties_get_int( filter_props, "window" );
424         if ( mlt_properties_get( filter_props, "smooth_buffer" ) == NULL && window > 1 )
425         {
426                 // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
427                 double *smooth_buffer = (double*) calloc( window, sizeof( double ) );
428                 int i;
429                 for ( i = 0; i < window; i++ )
430                         smooth_buffer[ i ] = -1.0;
431                 mlt_properties_set_data( filter_props, "smooth_buffer", smooth_buffer, 0, free, NULL );
432         }
433         
434         // Push the filter onto the stack
435         mlt_frame_push_audio( frame, this );
436
437         // Override the get_audio method
438         mlt_frame_push_audio( frame, filter_get_audio );
439
440         return frame;
441 }
442
443 /** Constructor for the filter.
444 */
445
446 mlt_filter filter_volume_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
447 {
448         mlt_filter this = calloc( 1, sizeof( struct mlt_filter_s ) );
449         if ( this != NULL && mlt_filter_init( this, NULL ) == 0 )
450         {
451                 mlt_properties properties = MLT_FILTER_PROPERTIES( this );
452                 this->process = filter_process;
453                 if ( arg != NULL )
454                         mlt_properties_set( properties, "gain", arg );
455
456                 mlt_properties_set_int( properties, "window", 75 );
457                 mlt_properties_set( properties, "max_gain", "20dB" );
458         }
459         return this;
460 }