]> git.sesse.net Git - mlt/blob - src/modules/core/transition_mix.c
Fix reading binary files on Windows.
[mlt] / src / modules / core / transition_mix.c
1 /*
2  * transition_mix.c -- mix two audio streams
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Dan Dennedy <dan@dennedy.org>
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_transition.h>
22 #include <framework/mlt_frame.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28
29
30 static int mix_audio( mlt_frame this, mlt_frame that, float weight_start, float weight_end, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
31 {
32         int ret = 0;
33         int16_t *src, *dest;
34         int frequency_src = *frequency, frequency_dest = *frequency;
35         int channels_src = *channels, channels_dest = *channels;
36         int samples_src = *samples, samples_dest = *samples;
37         int i, j;
38         double d = 0, s = 0;
39
40         mlt_frame_get_audio( that, (void**) &src, format, &frequency_src, &channels_src, &samples_src );
41         mlt_frame_get_audio( this, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest );
42
43         int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
44         mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
45         if ( silent )
46                 memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) );
47
48         silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" );
49         mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 );
50         if ( silent )
51                 memset( src, 0, samples_src * channels_src * sizeof( int16_t ) );
52
53         if ( channels_src > 6 )
54                 channels_src = 0;
55         if ( channels_dest > 6 )
56                 channels_dest = 0;
57         if ( samples_src > 4000 )
58                 samples_src = 0;
59         if ( samples_dest > 4000 )
60                 samples_dest = 0;
61
62         // determine number of samples to process
63         *samples = samples_src < samples_dest ? samples_src : samples_dest;
64         *channels = channels_src < channels_dest ? channels_src : channels_dest;
65         *buffer = dest;
66         *frequency = frequency_dest;
67
68         // Compute a smooth ramp over start to end
69         float weight = weight_start;
70         float weight_step = ( weight_end - weight_start ) / *samples;
71
72         if ( src == dest )
73         {
74                 *samples = samples_src;
75                 *channels = channels_src;
76                 *buffer = src;
77                 *frequency = frequency_src;
78                 return ret;
79         }
80
81         // Mixdown
82         for ( i = 0; i < *samples; i++ )
83         {
84                 for ( j = 0; j < *channels; j++ )
85                 {
86                         if ( j < channels_dest )
87                                 d = (double) dest[ i * channels_dest + j ];
88                         if ( j < channels_src )
89                                 s = (double) src[ i * channels_src + j ];
90                         dest[ i * channels_dest + j ] = s * weight + d * ( 1.0 - weight );
91                 }
92                 weight += weight_step;
93         }
94
95         return ret;
96 }
97
98 // Replacement for broken mlt_frame_audio_mix - this filter uses an inline low pass filter
99 // to allow mixing without volume hacking
100 static int combine_audio( mlt_frame this, mlt_frame that, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
101 {
102         int ret = 0;
103         int16_t *src, *dest;
104         int frequency_src = *frequency, frequency_dest = *frequency;
105         int channels_src = *channels, channels_dest = *channels;
106         int samples_src = *samples, samples_dest = *samples;
107         int i, j;
108         double vp[ 6 ];
109         double b_weight = 1.0;
110
111         if ( mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "meta.mixdown" ) )
112                 b_weight = 1.0 - mlt_properties_get_double( MLT_FRAME_PROPERTIES( this ), "meta.volume" );
113
114         mlt_frame_get_audio( that, (void**) &src, format, &frequency_src, &channels_src, &samples_src );
115         mlt_frame_get_audio( this, (void**) &dest, format, &frequency_dest, &channels_dest, &samples_dest );
116
117         int silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( this ), "silent_audio" );
118         mlt_properties_set_int( MLT_FRAME_PROPERTIES( this ), "silent_audio", 0 );
119         if ( silent )
120                 memset( dest, 0, samples_dest * channels_dest * sizeof( int16_t ) );
121
122         silent = mlt_properties_get_int( MLT_FRAME_PROPERTIES( that ), "silent_audio" );
123         mlt_properties_set_int( MLT_FRAME_PROPERTIES( that ), "silent_audio", 0 );
124         if ( silent )
125                 memset( src, 0, samples_src * channels_src * sizeof( int16_t ) );
126
127         if ( src == dest )
128         {
129                 *samples = samples_src;
130                 *channels = channels_src;
131                 *buffer = src;
132                 *frequency = frequency_src;
133                 return ret;
134         }
135
136         // determine number of samples to process
137         *samples = samples_src < samples_dest ? samples_src : samples_dest;
138         *channels = channels_src < channels_dest ? channels_src : channels_dest;
139         *buffer = dest;
140         *frequency = frequency_dest;
141
142         for ( j = 0; j < *channels; j++ )
143                 vp[ j ] = ( double )dest[ j ];
144
145         double Fc = 0.5;
146         double B = exp(-2.0 * M_PI * Fc);
147         double A = 1.0 - B;
148         double v;
149
150         for ( i = 0; i < *samples; i++ )
151         {
152                 for ( j = 0; j < *channels; j++ )
153                 {
154                         v = ( double )( b_weight * dest[ i * channels_dest + j ] + src[ i * channels_src + j ] );
155                         v = v < -32767 ? -32767 : v > 32768 ? 32768 : v;
156                         vp[ j ] = dest[ i * channels_dest + j ] = ( int16_t )( v * A + vp[ j ] * B );
157                 }
158         }
159
160         return ret;
161 }
162
163 /** Get the audio.
164 */
165
166 static int transition_get_audio( mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples )
167 {
168         // Get the b frame from the stack
169         mlt_frame b_frame = mlt_frame_pop_audio( frame );
170
171         // Get the effect
172         mlt_transition effect = mlt_frame_pop_audio( frame );
173
174         // Get the properties of the b frame
175         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
176
177         // We can only mix s16
178         *format = mlt_audio_s16;
179
180         if ( mlt_properties_get_int( MLT_TRANSITION_PROPERTIES( effect ), "combine" ) == 0 )
181         {
182                 double mix_start = 0.5, mix_end = 0.5;
183                 if ( mlt_properties_get( b_props, "audio.previous_mix" ) != NULL )
184                         mix_start = mlt_properties_get_double( b_props, "audio.previous_mix" );
185                 if ( mlt_properties_get( b_props, "audio.mix" ) != NULL )
186                         mix_end = mlt_properties_get_double( b_props, "audio.mix" );
187                 if ( mlt_properties_get_int( b_props, "audio.reverse" ) )
188                 {
189                         mix_start = 1 - mix_start;
190                         mix_end = 1 - mix_end;
191                 }
192
193                 mix_audio( frame, b_frame, mix_start, mix_end, buffer, format, frequency, channels, samples );
194         }
195         else
196         {
197                 combine_audio( frame, b_frame, buffer, format, frequency, channels, samples );
198         }
199
200         return 0;
201 }
202
203
204 /** Mix transition processing.
205 */
206
207 static mlt_frame transition_process( mlt_transition this, mlt_frame a_frame, mlt_frame b_frame )
208 {
209         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
210         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
211
212         // Only if mix is specified, otherwise a producer may set the mix
213         if ( mlt_properties_get( properties, "start" ) != NULL )
214         {
215                 // Determine the time position of this frame in the transition duration
216                 mlt_properties props = mlt_properties_get_data( MLT_FRAME_PROPERTIES( b_frame ), "_producer", NULL );
217                 mlt_position in = mlt_properties_get_int( props, "in" );
218                 mlt_position out = mlt_properties_get_int( props, "out" );
219                 int length = mlt_properties_get_int(  MLT_TRANSITION_PROPERTIES( this ), "length" );
220                 mlt_position time = mlt_properties_get_int( props, "_frame" );
221                 double mix = mlt_transition_get_progress( this, b_frame );
222                 if ( mlt_properties_get_int(  properties, "always_active" ) )
223                         mix = ( double ) ( time - in ) / ( double ) ( out - in + 1 );
224
225                 // TODO: Check the logic here - shouldn't we be computing current and next mixing levels in all cases?
226                 if ( length == 0 )
227                 {
228                         // If there is an end mix level adjust mix to the range
229                         if ( mlt_properties_get( properties, "end" ) != NULL )
230                         {
231                                 double start = mlt_properties_get_double( properties, "start" );
232                                 double end = mlt_properties_get_double( properties, "end" );
233                                 mix = start + ( end - start ) * mix;
234                         }
235                         // A negative means total crossfade (uses position)
236                         else if ( mlt_properties_get_double( properties, "start" ) >= 0 )
237                         {
238                                 // Otherwise, start/constructor is a constant mix level
239                         mix = mlt_properties_get_double( properties, "start" );
240                         }
241                 
242                         // Finally, set the mix property on the frame
243                         mlt_properties_set_double( b_props, "audio.mix", mix );
244         
245                         // Initialise transition previous mix value to prevent an inadvertant jump from 0
246                         mlt_position last_position = mlt_properties_get_position( properties, "_last_position" );
247                         mlt_position current_position = mlt_frame_get_position( b_frame );
248                         mlt_properties_set_position( properties, "_last_position", current_position );
249                         if ( mlt_properties_get( properties, "_previous_mix" ) == NULL
250                              || current_position != last_position + 1 )
251                                 mlt_properties_set_double( properties, "_previous_mix", mix );
252                                 
253                         // Tell b frame what the previous mix level was
254                         mlt_properties_set_double( b_props, "audio.previous_mix", mlt_properties_get_double( properties, "_previous_mix" ) );
255
256                         // Save the current mix level for the next iteration
257                         mlt_properties_set_double( properties, "_previous_mix", mlt_properties_get_double( b_props, "audio.mix" ) );
258                 
259                         mlt_properties_set_double( b_props, "audio.reverse", mlt_properties_get_double( properties, "reverse" ) );
260                 }
261                 else
262                 {
263                         double level = mlt_properties_get_double( properties, "start" );
264                         double mix_start = level;
265                         double mix_end = mix_start;
266                         double mix_increment = 1.0 / length;
267                         if ( time - in < length )
268                         {
269                                 mix_start = mix_start * ( ( double )( time - in ) / length );
270                                 mix_end = mix_start + mix_increment;
271                         }
272                         else if ( time > out - length )
273                         {
274                                 mix_end = mix_start * ( ( double )( out - time - in ) / length );
275                                 mix_start = mix_end - mix_increment;
276                         }
277
278                         mix_start = mix_start < 0 ? 0 : mix_start > level ? level : mix_start;
279                         mix_end = mix_end < 0 ? 0 : mix_end > level ? level : mix_end;
280                         mlt_properties_set_double( b_props, "audio.previous_mix", mix_start );
281                         mlt_properties_set_double( b_props, "audio.mix", mix_end );
282                 }
283         }
284
285         // Override the get_audio method
286         mlt_frame_push_audio( a_frame, this );
287         mlt_frame_push_audio( a_frame, b_frame );
288         mlt_frame_push_audio( a_frame, transition_get_audio );
289         
290         return a_frame;
291 }
292
293 /** Constructor for the transition.
294 */
295
296 mlt_transition transition_mix_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
297 {
298         mlt_transition this = calloc( 1, sizeof( struct mlt_transition_s ) );
299         if ( this != NULL && mlt_transition_init( this, NULL ) == 0 )
300         {
301                 this->process = transition_process;
302                 if ( arg != NULL )
303                         mlt_properties_set_double( MLT_TRANSITION_PROPERTIES( this ), "start", atof( arg ) );
304                 // Inform apps and framework that this is an audio only transition
305                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( this ), "_transition_type", 2 );
306         }
307         return this;
308 }
309