]> git.sesse.net Git - mlt/blob - src/modules/plus/filter_loudness.c
fb8683f458d4da99686143b9d90f550c3839adfd
[mlt] / src / modules / plus / filter_loudness.c
1 /*
2  * filter_ebur128.c -- normalize audio according to EBU R128
3  * Copyright (C) 2014 Brian Matherly <pez4brian@yahoo.com>
4  * Author: Brian Matherly <pez4brian@yahoo.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <framework/mlt.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <math.h>
25 #include "ebur128/ebur128.h"
26
27 #define MAX_RESULT_SIZE 512
28
29 typedef struct
30 {
31         ebur128_state* state;
32 } analyze_data;
33
34 typedef struct
35 {
36         double in_loudness;
37         double in_range;
38         double in_peak;
39         double coeff;
40 } apply_data;
41
42 typedef struct
43 {
44         analyze_data* analyze;
45         apply_data* apply;
46         mlt_position last_position;
47 } private_data;
48
49 static void destroy_analyze_data( mlt_filter filter )
50 {
51         private_data* private = (private_data*)filter->child;
52         ebur128_destroy( &private->analyze->state );
53         free( private->analyze );
54         private->analyze = NULL;
55 }
56
57 static void init_analyze_data( mlt_filter filter, int channels, int samplerate )
58 {
59         private_data* private = (private_data*)filter->child;
60         private->analyze = (analyze_data*)calloc( 1, sizeof(analyze_data) );
61         private->analyze->state = ebur128_init( (unsigned int)channels, (unsigned long)samplerate, EBUR128_MODE_I | EBUR128_MODE_LRA | EBUR128_MODE_SAMPLE_PEAK );
62         private->last_position = 0;
63 }
64
65 static void destroy_apply_data( mlt_filter filter )
66 {
67         private_data* private = (private_data*)filter->child;
68         free( private->apply );
69         private->apply = NULL;
70 }
71
72 static void init_apply_data( mlt_filter filter )
73 {
74         private_data* private = (private_data*)filter->child;
75         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
76         char* results = mlt_properties_get( properties, "results" );
77         int scan_return = 0;
78
79         private->apply = (apply_data*)calloc( 1, sizeof(apply_data) );
80
81         scan_return = sscanf( results,"L: %lf\tR: %lf\tP %lf\n", &private->apply->in_loudness, &private->apply->in_range, &private->apply->in_peak );
82         if( scan_return != 3 )
83         {
84                 mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to load results: %s\n", results );
85                 destroy_apply_data( filter );
86                 return;
87         }
88         else
89         {
90                 double target_db = mlt_properties_get_double( properties, "program" );
91                 double delta_db = target_db - private->apply->in_loudness;
92                 private->apply->coeff = delta_db > -90.0f ? powf(10.0f, delta_db * 0.05f) : 0.0f;
93                 mlt_log_info( MLT_FILTER_SERVICE( filter ), "Loaded Results: L: %lf\tR: %lf\tP %lf\n", private->apply->in_loudness, private->apply->in_range, private->apply->in_peak );
94                 mlt_log_info( MLT_FILTER_SERVICE( filter ), "Coefficient: %lf\n", private->apply->coeff );
95         }
96 }
97
98 static void analyze( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
99 {
100         private_data* private = (private_data*)filter->child;
101         mlt_position pos = mlt_filter_get_position( filter, frame );
102
103         // If any frames are skipped, analysis data will be incomplete.
104         if( private->analyze && pos != private->last_position + 1 )
105         {
106                 mlt_log_error( MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n" );
107                 destroy_analyze_data( filter );
108         }
109
110         // Analyze Audio
111         if( !private->analyze && pos == 0 )
112         {
113                 init_analyze_data( filter, *channels, *frequency );
114         }
115
116         if( private->analyze )
117         {
118                 ebur128_add_frames_float( private->analyze->state, *buffer, *samples );
119
120                 if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
121                 {
122                         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
123                         double loudness = 0.0;
124                         double range = 0.0;
125                         double tmpPeak = 0.0;
126                         double peak = 0.0;
127                         int i = 0;
128                         char result[MAX_RESULT_SIZE];
129                         ebur128_loudness_global( private->analyze->state, &loudness );
130                         ebur128_loudness_range( private->analyze->state, &range );
131
132                         for ( i = 0; i < *channels; i++ )
133                         {
134                                 ebur128_sample_peak( private->analyze->state, i, &tmpPeak );
135                                 if( tmpPeak > peak )
136                                 {
137                                         peak = tmpPeak;
138                                 }
139                         }
140
141                         snprintf( result, MAX_RESULT_SIZE, "L: %lf\tR: %lf\tP %lf\n", loudness, range, peak );
142                         result[ MAX_RESULT_SIZE - 1 ] = '\0';
143                         mlt_log_info( MLT_FILTER_SERVICE( filter ), "Stored results: %s", result );
144                         mlt_properties_set( properties, "results", result );
145                         destroy_analyze_data( filter );
146                 }
147
148                 private->last_position = pos;
149         }
150 }
151
152 static void apply( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
153 {
154         private_data* private = (private_data*)filter->child;
155
156         // Analyze Audio
157         if( !private->apply )
158         {
159                 init_apply_data( filter );
160         }
161
162         if( private->apply )
163         {
164                 float* p = *buffer;
165                 int count = *samples * *channels;
166                 while( count-- )
167                 {
168                         *p = *p * private->apply->coeff;
169                         p++;
170                 }
171         }
172 }
173
174 /** Get the audio.
175 */
176
177 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
178 {
179         mlt_filter filter = mlt_frame_pop_audio( frame );
180         mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
181
182         mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
183
184         // Get the producer's audio
185         *format = mlt_audio_f32le;
186         mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
187
188         char* results = mlt_properties_get( properties, "results" );
189         if( results && strcmp( results, "" ) )
190         {
191                 apply( filter, frame, buffer, format, frequency, channels, samples );
192         }
193         else
194         {
195                 analyze( filter, frame, buffer, format, frequency, channels, samples );
196         }
197
198         mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
199
200         return 0;
201 }
202
203 /** Filter processing.
204 */
205
206 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
207 {
208         mlt_frame_push_audio( frame, filter );
209         mlt_frame_push_audio( frame, filter_get_audio );
210         return frame;
211 }
212
213 /** Destructor for the filter.
214 */
215
216 static void filter_close( mlt_filter filter )
217 {
218         private_data* private = (private_data*)filter->child;
219
220         if ( private )
221         {
222                 if ( private->analyze )
223                 {
224                         destroy_analyze_data( filter );
225                 }
226                 free( private );
227         }
228         filter->child = NULL;
229         filter->close = NULL;
230         filter->parent.close = NULL;
231         mlt_service_close( &filter->parent );
232 }
233
234 /** Constructor for the filter.
235 */
236
237 mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
238 {
239         mlt_filter filter = mlt_filter_new( );
240         private_data* data = (private_data*)calloc( 1, sizeof(private_data) );
241
242         if ( filter && data )
243         {
244                 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
245                 mlt_properties_set( properties, "program", "-23.0" );
246
247                 data->analyze = NULL;
248
249                 filter->close = filter_close;
250                 filter->process = filter_process;
251                 filter->child = data;
252         }
253         else
254         {
255                 if( filter )
256                 {
257                         mlt_filter_close( filter );
258                         filter = NULL;
259                 }
260
261                 if( data )
262                 {
263                         free( data );
264                 }
265         }
266
267         return filter;
268 }