1 /*
2  * filter_audiolevel.c -- get the audio level of each channel
3  * Copyright (C) 2002 Steve Harris
4  * Copyright (C) 2010 Marco Gittler <g.marco@freenet.de>
5  * Copyright (C) 2012 Dan Dennedy <dan@dennedy.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20  */
21 
22 #include <framework/mlt_filter.h>
23 #include <framework/mlt_frame.h>
24 #include <framework/mlt_log.h>
25 
26 #include <stdlib.h>
27 #include <math.h>
28 
29 #define AMPTODBFS(n) (log10(n) * 20.0)
30 
31 //----------------------------------------------------------------------------
32 // IEC standard dB scaling -- as borrowed from meterbridge (c) Steve Harris
33 
IEC_Scale(double dB)34 static inline double IEC_Scale(double dB)
35 {
36 	double fScale = 1.0f;
37 
38 	if (dB < -70.0f)
39 		fScale = 0.0f;
40 	else if (dB < -60.0f)                 //  0.0  ..   2.5
41 		fScale = (dB + 70.0f) * 0.0025f;
42 	else if (dB < -50.0f)                 //  2.5  ..   7.5
43 		fScale = (dB + 60.0f) * 0.005f + 0.025f;
44 	else if (dB < -40.0)                  //  7.5  ..  15.0
45 		fScale = (dB + 50.0f) * 0.0075f + 0.075f;
46 	else if (dB < -30.0f)                 // 15.0  ..  30.0
47 		fScale = (dB + 40.0f) * 0.015f + 0.15f;
48 	else if (dB < -20.0f)                 // 30.0  ..  50.0
49 		fScale = (dB + 30.0f) * 0.02f + 0.3f;
50 	else if (dB < -0.001f || dB > 0.001f) // 50.0  .. 100.0
51 		fScale = (dB + 20.0f) * 0.025f + 0.5f;
52 
53 	return fScale;
54 }
55 
filter_get_audio(mlt_frame frame,void ** buffer,mlt_audio_format * format,int * frequency,int * channels,int * samples)56 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
57 {
58 	mlt_filter filter = mlt_frame_pop_audio( frame );
59 
60 	// Get the properties from the filter
61 	mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter );
62 
63 	int iec_scale = mlt_properties_get_int( filter_props, "iec_scale" );
64 	*format = mlt_audio_s16;
65 	int error = mlt_frame_get_audio( frame, buffer, format, frequency, channels, samples );
66 	if ( error || !buffer ) return error;
67 
68 	int num_channels = *channels;
69 	int num_samples = *samples > 200 ? 200 : *samples;
70 	int num_oversample = 0;
71 	int c, s;
72 	char key[ 50 ];
73 	int16_t *pcm = (int16_t*) *buffer;
74 
75 	for ( c = 0; c < *channels; c++ )
76 	{
77 		double val = 0;
78 		double level = 0.0;
79 
80 		for ( s = 0; s < num_samples; s++ )
81 		{
82 			double sample = fabs( pcm[c + s * num_channels] / 128.0 );
83 			val += sample;
84 			if ( sample == 128 )
85 				num_oversample++;
86 			else
87 				num_oversample = 0;
88 			// 10 samples @max => show max signal
89 			if ( num_oversample > 10 )
90 			{
91 				level = 1.0;
92 				break;
93 			}
94 			// if 3 samples over max => 1 peak over 0 db (0 dB = 40.0)
95 			if ( num_oversample > 3 )
96 				level = 41.0/42.0;
97 		}
98 		// max amplitude = 40/42, 3to10  oversamples=41, more then 10 oversamples=42
99 		if ( level == 0.0 && num_samples > 0 )
100 			level = val / num_samples * 40.0/42.0 / 127.0;
101 		if ( iec_scale )
102 			level = IEC_Scale( AMPTODBFS( level ) );
103 		sprintf( key, "meta.media.audio_level.%d", c );
104 		mlt_properties_set_double( MLT_FRAME_PROPERTIES( frame ), key, level );
105 		sprintf( key, "_audio_level.%d", c );
106 		mlt_properties_set_double( filter_props, key, level );
107 		mlt_log_debug( MLT_FILTER_SERVICE( filter ), "channel %d level %f\n", c, level );
108 	}
109 	mlt_properties_set_position( filter_props, "_position", mlt_filter_get_position( filter, frame ) );
110 
111 	return error;
112 }
113 
114 /** Filter processing.
115 */
116 
filter_process(mlt_filter filter,mlt_frame frame)117 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
118 {
119 	mlt_frame_push_audio( frame, filter );
120 	mlt_frame_push_audio( frame, filter_get_audio );
121 	return frame;
122 }
123 
124 /** Constructor for the filter.
125 */
126 
filter_audiolevel_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)127 mlt_filter filter_audiolevel_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
128 {
129 	mlt_filter filter = mlt_filter_new();
130 	if ( filter )
131 	{
132 		filter->process = filter_process;
133 		mlt_properties_set_int( MLT_FILTER_PROPERTIES(filter), "iec_scale", 1 );
134 	}
135 	return filter;
136 }
137