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