2 * filter_panner.c --pan/balance audio channels
3 * Copyright (C) 2010 Ushodaya Enterprises Limited
4 * Author: Dan Dennedy <dan@dennedy.org>
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_filter.h>
22 #include <framework/mlt_frame.h>
23 #include <framework/mlt_log.h>
34 static int filter_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
36 mlt_properties properties = mlt_frame_pop_audio( frame );
37 mlt_filter filter = mlt_frame_pop_audio( frame );
38 mlt_properties filter_props = MLT_FILTER_PROPERTIES( filter );
39 mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
41 // We can only mix s16
42 *format = mlt_audio_s16;
43 mlt_frame_get_audio( frame, (void**) buffer, format, frequency, channels, samples );
46 int silent = mlt_properties_get_int( frame_props, "silent_audio" );
47 mlt_properties_set_int( frame_props, "silent_audio", 0 );
49 memset( *buffer, 0, *samples * *channels * sizeof( int16_t ) );
52 int16_t *src = mlt_properties_get_data( filter_props, "scratch_buffer", &src_size );
53 int16_t *dest = *buffer;
54 double v; // sample accumulator
56 double factors[6][6]; // mixing weights [in][out]
57 double mix_start = 0.5, mix_end = 0.5;
58 if ( mlt_properties_get( properties, "previous_mix" ) != NULL )
59 mix_start = mlt_properties_get_double( properties, "previous_mix" );
60 if ( mlt_properties_get( properties, "mix" ) != NULL )
61 mix_end = mlt_properties_get_double( properties, "mix" );
62 double weight = mix_start;
63 double weight_step = ( mix_end - mix_start ) / *samples;
64 int active_channel = mlt_properties_get_int( properties, "channel" );
65 int gang = mlt_properties_get_int( properties, "gang" ) ? 2 : 1;
66 // Use an inline low-pass filter to help avoid clipping
68 double B = exp(-2.0 * M_PI * Fc);
72 // Setup or resize a scratch buffer
73 if ( !src || src_size < *samples * *channels * sizeof(int16_t) )
75 // We allocate 4 more samples than we need to deal with jitter in the sample count per frame.
76 src_size = ( *samples + 4 ) * *channels * sizeof(int16_t);
77 src = mlt_pool_alloc( src_size );
80 mlt_properties_set_data( filter_props, "scratch_buffer", src, src_size, mlt_pool_release, NULL );
83 // We must use a pristine copy as the source
84 memcpy( src, *buffer, *samples * *channels * sizeof(int16_t) );
86 // Initialize the mix factors
87 for ( i = 0; i < 6; i++ )
88 for ( out = 0; out < 6; out++ )
89 factors[i][out] = 0.0;
91 for ( out = 0; out < *channels; out++ )
92 vp[out] = (double) dest[out];
94 for ( i = 0; i < *samples; i++ )
96 // Recompute the mix factors
97 switch ( active_channel )
99 case -1: // Front L/R balance
100 case -2: // Rear L/R balance
102 // Gang front/rear balance if requested
103 int g, active = active_channel;
104 for ( g = 0; g < gang; g++, active-- )
106 int left = active == -1 ? 0 : 2;
107 int right = left + 1;
110 factors[left][left] = 1.0;
111 factors[right][right] = weight + 1.0 < 0.0 ? 0.0 : weight + 1.0;
115 factors[left][left] = 1.0 - weight < 0.0 ? 0.0 : 1.0 - weight;
116 factors[right][right] = 1.0;
121 case -3: // Left fade
122 case -4: // right fade
124 // Gang left/right fade if requested
125 int g, active = active_channel;
126 for ( g = 0; g < gang; g++, active-- )
128 int front = active == -3 ? 0 : 1;
129 int rear = front + 2;
132 factors[front][front] = 1.0;
133 factors[rear][rear] = weight + 1.0 < 0.0 ? 0.0 : weight + 1.0;
137 factors[front][front] = 1.0 - weight < 0.0 ? 0.0 : 1.0 - weight;
138 factors[rear][rear] = 1.0;
146 int left = active_channel;
147 int right = left + 1;
148 factors[right][right] = 1.0;
149 if ( weight < 0.0 ) // output left toward left
151 factors[left][left] = 0.5 - weight * 0.5;
152 factors[left][right] = ( 1.0 + weight ) * 0.5;
154 else // output left toward right
156 factors[left][left] = ( 1.0 - weight ) * 0.5;
157 factors[left][right] = 0.5 + weight * 0.5;
164 int right = active_channel;
165 int left = right - 1;
166 factors[left][left] = 1.0;
167 if ( weight < 0.0 ) // output right toward left
169 factors[right][left] = 0.5 - weight * 0.5;
170 factors[right][right] = ( 1.0 + weight ) * 0.5;
172 else // output right toward right
174 factors[right][left] = ( 1.0 - weight ) * 0.5;
175 factors[right][right] = 0.5 + weight * 0.5;
182 for ( out = 0; out < *channels && out < 6; out++ )
185 for ( in = 0; in < *channels && in < 6; in++ )
186 v += factors[in][out] * src[ i * *channels + in ];
187 // dest[ i * *channels + out ] = (int16_t) ( v < -32767 ? -32767 : v > 32768 ? 32768 : v );
188 v = v < -32767 ? -32767 : v > 32768 ? 32768 : v;
189 vp[out] = dest[ i * *channels + out ] = (int16_t) ( v * A + vp[ out ] * B );
191 weight += weight_step;
198 /** Pan filter processing.
201 static mlt_frame filter_process( mlt_filter filter, mlt_frame frame )
203 mlt_properties properties = MLT_FILTER_PROPERTIES( filter );
204 mlt_properties frame_props = MLT_FRAME_PROPERTIES( frame );
205 mlt_properties instance_props = mlt_properties_new();
207 // Only if mix is specified, otherwise a producer may set the mix
208 if ( mlt_properties_get( properties, "start" ) != NULL )
210 // Determine the time position of this frame in the filter duration
211 mlt_properties props = mlt_properties_get_data( frame_props, "_producer", NULL );
212 int always_active = mlt_properties_get_int( properties, "always_active" );
213 mlt_position in = !always_active ? mlt_filter_get_in( filter ) : mlt_properties_get_int( props, "in" );
214 mlt_position out = !always_active ? mlt_filter_get_out( filter ) : mlt_properties_get_int( props, "out" );
215 int length = mlt_properties_get_int( properties, "length" );
216 mlt_position time = !always_active ? mlt_frame_get_position( frame ) : mlt_properties_get_int( props, "_frame" );
217 double mix = ( double )( time - in ) / ( double )( out - in + 1 );
221 // If there is an end mix level adjust mix to the range
222 if ( mlt_properties_get( properties, "end" ) != NULL )
224 double start = mlt_properties_get_double( properties, "start" );
225 double end = mlt_properties_get_double( properties, "end" );
226 mix = start + ( end - start ) * mix;
228 // Use constant mix level if only start
229 else if ( mlt_properties_get( properties, "start" ) != NULL )
231 mix = mlt_properties_get_double( properties, "start" );
234 // Convert it from [0, 1] to [-1, 1]
235 mix = mix * 2.0 - 1.0;
237 // Finally, set the mix property on the frame
238 mlt_properties_set_double( instance_props, "mix", mix );
240 // Initialise filter previous mix value to prevent an inadvertant jump from 0
241 mlt_position last_position = mlt_properties_get_position( properties, "_last_position" );
242 mlt_position current_position = mlt_frame_get_position( frame );
243 mlt_properties_set_position( properties, "_last_position", current_position );
244 if ( mlt_properties_get( properties, "_previous_mix" ) == NULL
245 || current_position != last_position + 1 )
246 mlt_properties_set_double( properties, "_previous_mix", mix );
248 // Tell the frame what the previous mix level was
249 mlt_properties_set_double( instance_props, "previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) );
251 // Save the current mix level for the next iteration
252 mlt_properties_set_double( properties, "_previous_mix", mix );
256 double level = mlt_properties_get_double( properties, "start" );
257 double mix_start = level;
258 double mix_end = mix_start;
259 double mix_increment = 1.0 / length;
260 if ( time - in < length )
262 mix_start *= ( double )( time - in ) / length;
263 mix_end = mix_start + mix_increment;
265 else if ( time > out - length )
267 mix_end = mix_start * ( ( double )( out - time - in ) / length );
268 mix_start = mix_end - mix_increment;
271 mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start;
272 mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end;
273 mlt_properties_set_double( instance_props, "previous_mix", mix_start );
274 mlt_properties_set_double( instance_props, "mix", mix_end );
276 mlt_properties_set_int( instance_props, "channel", mlt_properties_get_int( properties, "channel" ) );
277 mlt_properties_set_int( instance_props, "gang", mlt_properties_get_int( properties, "gang" ) );
279 mlt_properties_set_data( frame_props, mlt_properties_get( properties, "_unique_id" ),
280 instance_props, 0, (mlt_destructor) mlt_properties_close, NULL );
282 // Override the get_audio method
283 mlt_frame_push_audio( frame, filter );
284 mlt_frame_push_audio( frame, instance_props );
285 mlt_frame_push_audio( frame, filter_get_audio );
290 /** Constructor for the filter.
293 mlt_filter filter_panner_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
295 mlt_filter filter = calloc( 1, sizeof( struct mlt_filter_s ) );
296 if ( filter != NULL && mlt_filter_init( filter, NULL ) == 0 )
298 filter->process = filter_process;
300 mlt_properties_set_double( MLT_FILTER_PROPERTIES( filter ), "start", atof( arg ) );
301 mlt_properties_set_int( MLT_FILTER_PROPERTIES( filter ), "channel", -1 );