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>
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.
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.
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
21 #include <framework/mlt.h>
25 #include "ebur128/ebur128.h"
27 #define MAX_RESULT_SIZE 512
44 analyze_data* analyze;
46 mlt_position last_position;
49 static void destroy_analyze_data( mlt_filter filter )
51 private_data* private = (private_data*)filter->child;
52 ebur128_destroy( &private->analyze->state );
53 free( private->analyze );
54 private->analyze = NULL;
57 static void init_analyze_data( mlt_filter filter, int channels, int samplerate )
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;
65 static void destroy_apply_data( mlt_filter filter )
67 private_data* private = (private_data*)filter->child;
68 free( private->apply );
69 private->apply = NULL;
72 static void init_apply_data( mlt_filter filter )
74 private_data* private = (private_data*)filter->child;
75 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
76 char* results = mlt_properties_get( properties, "results" );
79 private->apply = (apply_data*)calloc( 1, sizeof(apply_data) );
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 )
84 mlt_log_error( MLT_FILTER_SERVICE( filter ), "Unable to load results: %s\n", results );
85 destroy_apply_data( filter );
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 );
98 static void analyze( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
100 private_data* private = (private_data*)filter->child;
101 mlt_position pos = mlt_filter_get_position( filter, frame );
103 // If any frames are skipped, analysis data will be incomplete.
104 if( private->analyze && pos != private->last_position + 1 )
106 mlt_log_error( MLT_FILTER_SERVICE(filter), "Analysis Failed: Bad frame sequence\n" );
107 destroy_analyze_data( filter );
111 if( !private->analyze && pos == 0 )
113 init_analyze_data( filter, *channels, *frequency );
116 if( private->analyze )
118 ebur128_add_frames_float( private->analyze->state, *buffer, *samples );
120 if ( pos + 1 == mlt_filter_get_length2( filter, frame ) )
122 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
123 double loudness = 0.0;
125 double tmpPeak = 0.0;
128 char result[MAX_RESULT_SIZE];
129 ebur128_loudness_global( private->analyze->state, &loudness );
130 ebur128_loudness_range( private->analyze->state, &range );
132 for ( i = 0; i < *channels; i++ )
134 ebur128_sample_peak( private->analyze->state, i, &tmpPeak );
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 );
148 private->last_position = pos;
152 static void apply( mlt_filter filter, mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
154 private_data* private = (private_data*)filter->child;
157 if( !private->apply )
159 init_apply_data( filter );
165 int count = *samples * *channels;
168 *p = *p * private->apply->coeff;
177 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
179 mlt_filter filter = mlt_frame_pop_audio( frame );
180 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
182 mlt_service_lock( MLT_FILTER_SERVICE( filter ) );
184 // Get the producer's audio
185 *format = mlt_audio_f32le;
186 mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
188 char* results = mlt_properties_get( properties, "results" );
189 if( results && strcmp( results, "" ) )
191 apply( filter, frame, buffer, format, frequency, channels, samples );
195 analyze( filter, frame, buffer, format, frequency, channels, samples );
198 mlt_service_unlock( MLT_FILTER_SERVICE( filter ) );
203 /** Filter processing.
206 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
208 mlt_frame_push_audio( frame, filter );
209 mlt_frame_push_audio( frame, filter_get_audio );
213 /** Destructor for the filter.
216 static void filter_close( mlt_filter filter )
218 private_data* private = (private_data*)filter->child;
222 if ( private->analyze )
224 destroy_analyze_data( filter );
228 filter->child = NULL;
229 filter->close = NULL;
230 filter->parent.close = NULL;
231 mlt_service_close( &filter->parent );
234 /** Constructor for the filter.
237 mlt_filter filter_loudness_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
239 mlt_filter filter = mlt_filter_new( );
240 private_data* data = (private_data*)calloc( 1, sizeof(private_data) );
242 if ( filter && data )
244 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
245 mlt_properties_set( properties, "program", "-23.0" );
247 data->analyze = NULL;
249 filter->close = filter_close;
250 filter->process = filter_process;
251 filter->child = data;
257 mlt_filter_close( filter );