comment some diagnostics
[melted] / src / modules / core / 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 "filter_volume.h"
22
23 #include <framework/mlt_frame.h>
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <math.h>
28 #include <ctype.h>
29 #include <string.h>
30
31 #define MAX_CHANNELS 6
32 #define SMOOTH_BUFFER_SIZE 75 /* smooth over 3 seconds on PAL */
33 #define EPSILON 0.00001
34
35 /* The normalise functions come from the normalize utility:
36 Copyright (C) 1999--2002 Chris Vaill */
37
38 #define samp_width 16
39
40 #ifndef ROUND
41 # define ROUND(x) floor((x) + 0.5)
42 #endif
43
44 #define DBFSTOAMP(x) pow(10,(x)/20.0)
45
46 /** Return nonzero if the two strings are equal, ignoring case, up to
47 the first n characters.
48 */
49 int strncaseeq(const char *s1, const char *s2, size_t n)
50 {
51 for ( ; n > 0; n--)
52 {
53 if (tolower(*s1++) != tolower(*s2++))
54 return 0;
55 }
56 return 1;
57 }
58
59 /** Limiter function.
60
61 / tanh((x + lev) / (1-lev)) * (1-lev) - lev (for x < -lev)
62 |
63 x' = | x (for |x| <= lev)
64 |
65 \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
66
67 With limiter level = 0, this is equivalent to a tanh() function;
68 with limiter level = 1, this is equivalent to clipping.
69 */
70 static inline double limiter( double x, double lmtr_lvl )
71 {
72 double xp = x;
73
74 if (x < -lmtr_lvl)
75 xp = tanh((x + lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) - lmtr_lvl;
76 else if (x > lmtr_lvl)
77 xp = tanh((x - lmtr_lvl) / (1-lmtr_lvl)) * (1-lmtr_lvl) + lmtr_lvl;
78
79 // if ( x != xp )
80 // fprintf( stderr, "filter_volume: sample %f limited %f\n", x, xp );
81
82 return xp;
83 }
84
85
86 /** Takes a full smoothing window, and returns the value of the center
87 element, smoothed.
88
89 Currently, just does a mean filter, but we could do a median or
90 gaussian filter here instead.
91 */
92 static inline double get_smoothed_data( double *buf, int count )
93 {
94 int i, j;
95 double smoothed = 0;
96
97 for ( i = 0, j = 0; i < count; i++ )
98 {
99 if ( buf[ i ] != -1.0 )
100 {
101 smoothed += buf[ i ];
102 j++;
103 }
104 }
105 smoothed /= j;
106 // fprintf( stderr, "smoothed over %d values, result %f\n", j, smoothed );
107
108 return smoothed;
109 }
110
111 /** Get the max power level (using RMS) and peak level of the audio segment.
112 */
113 double signal_max_power( int16_t *buffer, int channels, int samples, int16_t *peak )
114 {
115 // Determine numeric limits
116 int bytes_per_samp = (samp_width - 1) / 8 + 1;
117 int16_t max = (1 << (bytes_per_samp * 8 - 1)) - 1;
118 int16_t min = -max - 1;
119
120 double *sums = (double *) calloc( channels, sizeof(double) );
121 int c, i;
122 int16_t sample;
123 double pow, maxpow = 0;
124
125 /* initialize peaks to effectively -inf and +inf */
126 int16_t max_sample = min;
127 int16_t min_sample = max;
128
129 for ( i = 0; i < samples; i++ )
130 {
131 for ( c = 0; c < channels; c++ )
132 {
133 sample = *buffer++;
134 sums[ c ] += (double) sample * (double) sample;
135
136 /* track peak */
137 if ( sample > max_sample )
138 max_sample = sample;
139 else if ( sample < min_sample )
140 min_sample = sample;
141 }
142 }
143 for ( c = 0; c < channels; c++ )
144 {
145 pow = sums[ c ] / (double) samples;
146 if ( pow > maxpow )
147 maxpow = pow;
148 }
149
150 free( sums );
151
152 /* scale the pow value to be in the range 0.0 -- 1.0 */
153 maxpow /= ( (double) min * (double) min);
154
155 if ( -min_sample > max_sample )
156 *peak = min_sample / (double) min;
157 else
158 *peak = max_sample / (double) max;
159
160 return sqrt( maxpow );
161 }
162
163 /* ------ End normalize functions --------------------------------------- */
164
165 /** Get the audio.
166 */
167
168 static int filter_get_audio( mlt_frame frame, int16_t **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
169 {
170 // Get the properties of the a frame
171 mlt_properties properties = mlt_frame_properties( frame );
172 double gain = mlt_properties_get_double( properties, "gain" );
173 double max_gain = mlt_properties_get_double( properties, "volume.max_gain" );
174 double limiter_level = 0.5; /* -6 dBFS */
175 int normalise = mlt_properties_get_int( properties, "volume.normalise" );
176 double amplitude = mlt_properties_get_double( properties, "volume.amplitude" );
177 int i;
178 double sample;
179 int16_t peak;
180
181 if ( mlt_properties_get( properties, "volume.limiter" ) != NULL )
182 limiter_level = mlt_properties_get_double( properties, "volume.limiter" );
183
184 // Restore the original get_audio
185 frame->get_audio = mlt_properties_get_data( properties, "volume.get_audio", NULL );
186
187 // Get the producer's audio
188 mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
189
190 // Determine numeric limits
191 int bytes_per_samp = (samp_width - 1) / 8 + 1;
192 int samplemax = (1 << (bytes_per_samp * 8 - 1)) - 1;
193 int samplemin = -samplemax - 1;
194
195 if ( normalise )
196 {
197 double *smooth_buffer = mlt_properties_get_data( properties, "volume.smooth_buffer", NULL );
198 int *smooth_index = mlt_properties_get_data( properties, "volume.smooth_index", NULL );
199
200 // Compute the signal power and put into smoothing buffer
201 smooth_buffer[ *smooth_index ] = signal_max_power( *buffer, *channels, *samples, &peak );
202 // fprintf( stderr, "filter_volume: raw power %f ", smooth_buffer[ *smooth_index ] );
203 if ( smooth_buffer[ *smooth_index ] > EPSILON )
204 {
205 *smooth_index = ( *smooth_index + 1 ) % SMOOTH_BUFFER_SIZE;
206
207 // Smooth the data and compute the gain
208 // fprintf( stderr, "smoothed %f\n", get_smoothed_data( smooth_buffer, SMOOTH_BUFFER_SIZE ) );
209 gain *= amplitude / get_smoothed_data( smooth_buffer, SMOOTH_BUFFER_SIZE );
210 }
211 }
212
213 // if ( gain > 1.0 && normalise )
214 // fprintf(stderr, "filter_volume: limiter level %f gain %f\n", limiter_level, gain );
215
216 if ( max_gain > 0 && gain > max_gain )
217 gain = max_gain;
218
219 // Apply the gain
220 for ( i = 0; i < ( *channels * *samples ); i++ )
221 {
222 sample = (*buffer)[i] * gain;
223 (*buffer)[i] = ROUND( sample );
224
225 if ( gain > 1.0 )
226 {
227 /* use limiter function instead of clipping */
228 if ( normalise )
229 (*buffer)[i] = ROUND( samplemax * limiter( sample / (double) samplemax, limiter_level ) );
230
231 /* perform clipping */
232 else if ( sample > samplemax )
233 (*buffer)[i] = samplemax;
234 else if ( sample < samplemin )
235 (*buffer)[i] = samplemin;
236 }
237 }
238
239 return 0;
240 }
241
242 /** Filter processing.
243 */
244
245 static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
246 {
247 mlt_properties properties = mlt_frame_properties( frame );
248 mlt_properties filter_props = mlt_filter_properties( this );
249
250 // Propogate the gain property
251 if ( mlt_properties_get( properties, "gain" ) == NULL )
252 {
253 double gain = 1.0; // no adjustment
254
255 if ( mlt_properties_get( filter_props, "gain" ) != NULL )
256 {
257 char *p = mlt_properties_get( filter_props, "gain" );
258
259 if ( strncaseeq( p, "normalise", 9 ) )
260 mlt_properties_set( filter_props, "normalise", "" );
261 else
262 {
263 if ( strcmp( p, "" ) != 0 )
264 gain = fabs( strtod( p, &p) );
265
266 while ( isspace( *p ) )
267 p++;
268
269 /* check if "dB" is given after number */
270 if ( strncaseeq( p, "db", 2 ) )
271 gain = DBFSTOAMP( gain );
272 }
273 }
274 mlt_properties_set_double( properties, "gain", gain );
275 }
276
277 // Propogate the maximum gain property
278 if ( mlt_properties_get( filter_props, "max_gain" ) != NULL )
279 {
280 char *p = mlt_properties_get( filter_props, "max_gain" );
281 double gain = fabs( strtod( p, &p) ); // 0 = no max
282
283 while ( isspace( *p ) )
284 p++;
285
286 /* check if "dB" is given after number */
287 if ( strncaseeq( p, "db", 2 ) )
288 gain = DBFSTOAMP( gain );
289
290 mlt_properties_set_double( properties, "volume.max_gain", gain );
291 }
292
293 // Parse and propogate the limiter property
294 if ( mlt_properties_get( filter_props, "limiter" ) != NULL )
295 {
296 char *p = mlt_properties_get( filter_props, "limiter" );
297 double level = 0.5; /* -6dBFS */
298 if ( strcmp( p, "" ) != 0 )
299 level = strtod( p, &p);
300
301 while ( isspace( *p ) )
302 p++;
303
304 /* check if "dB" is given after number */
305 if ( strncaseeq( p, "db", 2 ) )
306 {
307 if ( level > 0 )
308 level = -level;
309 level = DBFSTOAMP( level );
310 }
311 else
312 {
313 if ( level < 0 )
314 level = -level;
315 }
316 mlt_properties_set_double( properties, "volume.limiter", level );
317 }
318
319 // Parse and propogate the normalise property
320 if ( mlt_properties_get( filter_props, "normalise" ) != NULL )
321 {
322 char *p = mlt_properties_get( filter_props, "normalise" );
323 double amplitude = 0.2511886431509580; /* -12dBFS */
324 if ( strcmp( p, "" ) != 0 )
325 amplitude = 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 {
333 if ( amplitude > 0 )
334 amplitude = -amplitude;
335 amplitude = DBFSTOAMP( amplitude );
336 }
337 else
338 {
339 if ( amplitude < 0 )
340 amplitude = -amplitude;
341 if ( amplitude > 1.0 )
342 amplitude = 1.0;
343 }
344 mlt_properties_set_int( properties, "volume.normalise", 1 );
345 mlt_properties_set_double( properties, "volume.amplitude", amplitude );
346 }
347
348 // Propogate the smoothing buffer properties
349 mlt_properties_set_data( properties, "volume.smooth_buffer",
350 mlt_properties_get_data( filter_props, "smooth_buffer", NULL ), 0, NULL, NULL );
351 mlt_properties_set_data( properties, "volume.smooth_index",
352 mlt_properties_get_data( filter_props, "smooth_index", NULL ), 0, NULL, NULL );
353
354 // Backup the original get_audio (it's still needed)
355 mlt_properties_set_data( properties, "volume.get_audio", frame->get_audio, 0, NULL, NULL );
356
357 // Override the get_audio method
358 frame->get_audio = filter_get_audio;
359
360 return frame;
361 }
362
363 /** Constructor for the filter.
364 */
365
366 mlt_filter filter_volume_init( char *arg )
367 {
368 mlt_filter this = calloc( sizeof( struct mlt_filter_s ), 1 );
369 if ( this != NULL && mlt_filter_init( this, NULL ) == 0 )
370 {
371 mlt_properties properties = mlt_filter_properties( this );
372 this->process = filter_process;
373 if ( arg != NULL )
374 mlt_properties_set( properties, "gain", arg );
375
376 // Create a smoothing buffer for the calculated "max power" of frame of audio used in normalisation
377 double *smooth_buffer = (double*) calloc( SMOOTH_BUFFER_SIZE, sizeof( double ) );
378 int i;
379 for ( i = 0; i < SMOOTH_BUFFER_SIZE; i++ )
380 smooth_buffer[ i ] = -1.0;
381 mlt_properties_set_data( mlt_filter_properties( this ), "smooth_buffer", smooth_buffer, 0, free, NULL );
382 int *smooth_index = calloc( 1, sizeof( int ) );
383 mlt_properties_set_data( mlt_filter_properties( this ), "smooth_index", smooth_index, 0, free, NULL );
384 }
385 return this;
386 }
387