Reported by Fedora team for gcc-13 and Andrew created patch here
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / floatautos.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * Copyright (C) 2003-2016 Cinelerra CV contributors
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
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #include "automation.inc"
24 #include "clip.h"
25 #include "edl.h"
26 #include "edlsession.h"
27 #include "filexml.h"
28 #include "floatauto.h"
29 #include "floatautos.h"
30 #include "track.h"
31 #include "localsession.h"
32 #include "transportque.inc"
33
34 FloatAutos::FloatAutos(EDL *edl,
35                                 Track *track,
36                                 float default_)
37  : Autos(edl, track)
38 {
39         this->default_ = default_;
40         type = AUTOMATION_TYPE_FLOAT;
41         float_min = -FLT_MAX;
42         float_max = FLT_MAX;
43 }
44
45 FloatAutos::~FloatAutos()
46 {
47 }
48
49 void FloatAutos::set_automation_mode(int64_t start, int64_t end, int mode)
50 {
51         FloatAuto *current = (FloatAuto*)first;
52         while(current)
53         {
54 // Is current auto in range?
55                 if(current->position >= start && current->position < end)
56                 {
57                         current->change_curve_mode((FloatAuto::t_mode)mode);
58                 }
59                 current = (FloatAuto*)NEXT;
60         }
61 }
62
63 void FloatAutos::draw_joining_line(BC_SubWindow *canvas, int vertical, int center_pixel, int x1, int y1, int x2, int y2)
64 {
65         if(vertical)
66                 canvas->draw_line(center_pixel - y1, x1, center_pixel - y2, x2);
67         else
68                 canvas->draw_line(x1, center_pixel + y1, x2, center_pixel + y2);
69 }
70
71 Auto* FloatAutos::new_auto()
72 {
73         FloatAuto *result = new FloatAuto(edl, this);
74         result->set_value(default_);
75         return result;
76 }
77
78 int FloatAutos::get_testy(float slope, int cursor_x, int ax, int ay)
79 {
80         return (int)(slope * (cursor_x - ax)) + ay;
81 }
82
83 int FloatAutos::automation_is_constant(int64_t start,
84         int64_t length,
85         int direction,
86         double &constant)
87 {
88         int total_autos = total();
89         int64_t end;
90         if(direction == PLAY_FORWARD)
91         {
92                 end = start + length;
93         }
94         else
95         {
96                 end = start + 1;
97                 start -= length;
98         }
99
100
101 // No keyframes on track
102         if(total_autos == 0)
103         {
104                 constant = ((FloatAuto*)default_auto)->get_value();
105                 return 1;
106         }
107         else
108 // Only one keyframe on track.
109         if(total_autos == 1)
110         {
111                 constant = ((FloatAuto*)first)->get_value();
112                 return 1;
113         }
114         else
115 // Last keyframe is before region
116         if(last->position <= start)
117         {
118                 constant = ((FloatAuto*)last)->get_value();
119                 return 1;
120         }
121         else
122 // First keyframe is after region
123         if(first->position > end)
124         {
125                 constant = ((FloatAuto*)first)->get_value();
126                 return 1;
127         }
128
129 // Scan sequentially
130         int64_t prev_position = -1;
131         for(Auto *current = first; current; current = NEXT)
132         {
133                 int test_current_next = 0;
134                 int test_previous_current = 0;
135                 FloatAuto *float_current = (FloatAuto*)current;
136
137 // keyframes before and after region but not in region
138                 if(prev_position >= 0 &&
139                         prev_position < start &&
140                         current->position >= end)
141                 {
142 // Get value now in case change doesn't occur
143                         constant = float_current->get_value();
144                         test_previous_current = 1;
145                 }
146                 prev_position = current->position;
147
148 // Keyframe occurs in the region
149                 if(!test_previous_current &&
150                         current->position < end &&
151                         current->position >= start)
152                 {
153
154 // Get value now in case change doesn't occur
155                         constant = float_current->get_value();
156
157 // Keyframe has neighbor
158                         if(current->previous)
159                         {
160                                 test_previous_current = 1;
161                         }
162
163                         if(current->next)
164                         {
165                                 test_current_next = 1;
166                         }
167                 }
168
169                 if(test_current_next)
170                 {
171 //printf("FloatAutos::automation_is_constant 1 %d\n", start);
172                         FloatAuto *float_next = (FloatAuto*)current->next;
173
174 // Change occurs between keyframes
175                         if( !EQUIV(float_current->get_value(), float_next->get_value()) ||
176                                 !EQUIV(float_current->get_control_out_value(), 0) ||
177                                 !EQUIV(float_next->get_control_in_value(), 0))
178                         {
179                                 return 0;
180                         }
181                 }
182
183                 if(test_previous_current)
184                 {
185                         FloatAuto *float_previous = (FloatAuto*)current->previous;
186
187 // Change occurs between keyframes
188                         if(!EQUIV(float_current->get_value(), float_previous->get_value()) ||
189                                 !EQUIV(float_current->get_control_in_value(), 0) ||
190                                 !EQUIV(float_previous->get_control_out_value(), 0))
191                         {
192 // printf("FloatAutos::automation_is_constant %d %d %d %f %f %f %f\n",
193 // start,
194 // float_previous->position,
195 // float_current->position,
196 // float_previous->get_value(),
197 // float_current->get_value(),
198 // float_previous->get_control_out_value(),
199 // float_current->get_control_in_value());
200                                 return 0;
201                         }
202                 }
203         }
204
205 // Got nothing that changes in the region.
206         return 1;
207 }
208
209 double FloatAutos::get_automation_constant(int64_t start, int64_t end)
210 {
211         Auto *current_auto, *before = 0, *after = 0;
212
213 // quickly get autos just outside range
214         get_neighbors(start, end, &before, &after);
215
216 // no auto before range so use first
217         if(before)
218                 current_auto = before;
219         else
220                 current_auto = first;
221
222 // no autos at all so use default value
223         if(!current_auto) current_auto = default_auto;
224
225         return ((FloatAuto*)current_auto)->get_value();
226 }
227
228
229 float FloatAutos::get_value(int64_t position,
230         int direction,
231         FloatAuto* &previous,
232         FloatAuto* &next)
233 {
234 // Calculate bezier equation at position
235         previous = (FloatAuto*)get_prev_auto(position, direction, (Auto* &)previous, 0);
236         next = (FloatAuto*)get_next_auto(position, direction, (Auto* &)next, 0);
237
238 // Constant
239         if( !next && !previous )
240                 return ((FloatAuto*)default_auto)->get_value();
241         if( next == previous )
242                 return previous->get_value();
243
244         if( direction == PLAY_FORWARD) {
245                 if( !previous ) return next->get_value(1);
246                 if( !next ) return previous->get_value(0);
247                 if( EQUIV(previous->get_value(0), next->get_value(1)) ) {
248                         if( (previous->curve_mode == FloatAuto::LINEAR &&
249                              next->curve_mode == FloatAuto::LINEAR) ||
250                             (EQUIV(previous->get_control_out_value(), 0) &&
251                              EQUIV(next->get_control_in_value(), 0)) ) {
252                                 return previous->get_value(0);
253                         }
254                 }
255         }
256         else if(direction == PLAY_REVERSE) {
257                 if( !previous ) return next->get_value(0);
258                 if( !next ) return previous->get_value(1);
259                 if( EQUIV(previous->get_value(0), next->get_value(1)) ) {
260                         if( (previous->curve_mode == FloatAuto::LINEAR &&
261                              next->curve_mode == FloatAuto::LINEAR) ||
262                             (EQUIV(previous->get_control_in_value(), 0) &&
263                              EQUIV(next->get_control_out_value(), 0)) ) {
264                                 return previous->get_value(1);
265                         }
266                 }
267         }
268 // at this point: previous and next not NULL, positions differ, value not constant.
269
270         return calculate_bezier(previous, next, position, direction);
271 }
272
273
274 float FloatAutos::calculate_bezier(FloatAuto *previous, FloatAuto *next,
275                 int64_t position, int direction)
276 {
277         int edge = direction == PLAY_FORWARD ? 0 : 1;
278         if( next->position == previous->position )
279                 return previous->get_value(edge);
280
281         float y0 = previous->get_value(edge);
282         float y3 = next->get_value(1-edge);
283
284 // control points
285         float y1 = y0 + (direction == PLAY_FORWARD ?
286                 previous->get_control_out_value() :
287                 previous->get_control_in_value());
288         float y2 = y3 + (direction == PLAY_FORWARD ?
289                 next->get_control_in_value() :
290                 next->get_control_out_value());
291         float t = (float)(position - previous->position) /
292                         (next->position - previous->position);
293
294         float tpow2 = t * t;
295         float tpow3 = t * t * t;
296         float invt = 1 - t;
297         float invtpow2 = invt * invt;
298         float invtpow3 = invt * invt * invt;
299
300         float result = (  invtpow3 * y0
301                 + 3 * t     * invtpow2 * y1
302                 + 3 * tpow2 * invt     * y2
303                 +     tpow3            * y3);
304 //printf("FloatAutos::get_value(t=%5.3f)->%6.2f   (prev,pos,next)=(%d,%d,%d)\n", t, result, previous->position, position, next->position);
305
306         return result;
307 }
308
309
310 float FloatAutos::calculate_bezier_derivation(FloatAuto *previous, FloatAuto *next, int64_t position)
311 // calculate the slope of the interpolating bezier function at given position.
312 // computed slope is based on the actual position scale (in frames or samples)
313 {
314         float scale = next->position - previous->position;
315         if( scale == 0 ) {
316                 if( !previous->get_control_out_position() )
317                         return 0;
318                 return previous->get_control_out_value() / previous->get_control_out_position();
319         }
320         float y0 = previous->get_value(0);
321         float y3 = next->get_value(1);
322
323 // control points
324         float y1 = y0 + previous->get_control_out_value();
325         float y2 = y3 + next->get_control_in_value();
326 // normalized scale
327         float t = (float)(position - previous->position) / scale;
328
329         float tpow2 = t * t;
330         float invt = 1 - t;
331         float invtpow2 = invt * invt;
332
333         float slope = 3 * (
334                 - invtpow2              * y0
335                 - invt * ( 2*t - invt ) * y1
336                 + t    * ( 2*invt - t ) * y2
337                 + tpow2                 * y3
338                 );
339
340         return slope / scale;
341 }
342
343
344
345 void FloatAutos::get_extents(float *min,
346         float *max,
347         int *coords_undefined,
348         int64_t unit_start,
349         int64_t unit_end)
350 {
351         if(!edl)
352         {
353                 printf("FloatAutos::get_extents edl == NULL\n");
354                 return;
355         }
356
357         if(!track)
358         {
359                 printf("FloatAutos::get_extents track == NULL\n");
360                 return;
361         }
362
363 // Use default auto
364         if(!first)
365         {
366                 FloatAuto *current = (FloatAuto*)default_auto;
367                 if(*coords_undefined)
368                 {
369                         *min = *max = current->get_value();
370                         *coords_undefined = 0;
371                 }
372
373                 *min = MIN(current->get_value(), *min);
374                 *max = MAX(current->get_value(), *max);
375         }
376
377 // Test all handles
378         for(FloatAuto *current = (FloatAuto*)first; current; current = (FloatAuto*)NEXT)
379         {
380                 if(current->position >= unit_start && current->position < unit_end)
381                 {
382                         if(*coords_undefined)
383                         {
384                                 *min = *max = current->get_value();
385                                 *coords_undefined = 0;
386                         }
387
388                         *min = MIN(current->get_value(), *min);
389                         *min = MIN(current->get_value() + current->get_control_in_value(), *min);
390                         *min = MIN(current->get_value() + current->get_control_out_value(), *min);
391
392                         *max = MAX(current->get_value(), *max);
393                         *max = MAX(current->get_value() + current->get_control_in_value(), *max);
394                         *max = MAX(current->get_value() + current->get_control_out_value(), *max);
395                 }
396         }
397
398 // Test joining regions
399         FloatAuto *prev = 0;
400         FloatAuto *next = 0;
401         int64_t unit_step = edl->local_session->zoom_sample;
402         if(track->data_type == TRACK_VIDEO)
403                 unit_step = (int64_t)(unit_step *
404                         edl->session->frame_rate /
405                         edl->session->sample_rate);
406         unit_step = MAX(unit_step, 1);
407         for(int64_t position = unit_start;
408                 position < unit_end;
409                 position += unit_step)
410         {
411                 float value = get_value(position,PLAY_FORWARD,prev,next);
412                 if(*coords_undefined)
413                 {
414                         *min = *max = value;
415                         *coords_undefined = 0;
416                 }
417                 else
418                 {
419                         *min = MIN(value, *min);
420                         *max = MAX(value, *max);
421                 }
422         }
423 }
424
425 void FloatAutos::set_proxy(int orig_scale, int new_scale)
426 {
427         float orig_value;
428         orig_value = ((FloatAuto*)default_auto)->value * orig_scale;
429         ((FloatAuto*)default_auto)->value = orig_value / new_scale;
430         orig_value = ((FloatAuto*)default_auto)->value1 * orig_scale;
431         ((FloatAuto*)default_auto)->value1 = orig_value / new_scale;
432
433         for( FloatAuto *current= (FloatAuto*)first; current; current=(FloatAuto*)NEXT ) {
434                 orig_value = current->value * orig_scale;
435                 current->value = orig_value / new_scale;
436                 orig_value = current->value1 * orig_scale;
437                 current->value1 = orig_value / new_scale;
438                 orig_value = current->control_in_value * orig_scale;
439                 current->control_in_value = orig_value / new_scale;
440                 orig_value = current->control_out_value * orig_scale;
441                 current->control_out_value = orig_value / new_scale;
442         }
443 }
444
445 void FloatAutos::dump()
446 {
447         printf("        FloatAutos::dump %p\n", this);
448         printf("        Default: position %jd value=%f\n",
449                 default_auto->position, ((FloatAuto*)default_auto)->get_value());
450         for(Auto* current = first; current; current = NEXT)
451         {
452                 printf("        position %jd value=%7.3f invalue=%7.3f outvalue=%7.3f %s\n",
453                         current->position,
454                         ((FloatAuto*)current)->get_value(),
455                         ((FloatAuto*)current)->get_control_in_value(),
456                         ((FloatAuto*)current)->get_control_out_value(),
457                         FloatAuto::curve_name(((FloatAuto*)current)->curve_mode));
458         }
459 }
460
461 double FloatAutos::automation_integral(int64_t start, int64_t length, int direction)
462 {
463         if( direction == PLAY_REVERSE )
464                 start -= length;
465         if( start < 0 ) {
466                 length += start;
467                 start = 0;
468         }
469         double value = 0;
470         int64_t pos = start, len = length, end = pos + len;
471         while( pos < end ) {
472                 int64_t prev_pos = 0, next_pos = end;
473                 Auto *zprev = 0, *znext = 0;
474                 FloatAuto *prev = (FloatAuto*)get_prev_auto(pos, direction, zprev, 0);
475                 if( prev ) prev_pos = prev->position;
476                 FloatAuto *next = (FloatAuto*)get_next_auto(pos, direction, znext, 0);
477                 if( next ) next_pos = next->position;
478                 if( !prev && !next ) prev = (FloatAuto*)default_auto;
479                 double dt = next_pos - prev_pos;
480                 double t0 = (pos - prev_pos) / dt;
481                 if( (pos = next_pos) > end ) pos = end;
482                 double t1 = (pos - prev_pos) / dt;
483
484                 double y0 = !prev ? next->get_value(1) : prev->get_value(0);
485                 double y1 = y0 + (!prev ? 0 : prev->get_control_out_value());
486                 double y3 = !next ? prev->get_value(0) : next->get_value(1);
487                 double y2 = y3 + (!next ? 0 : next->get_control_in_value());
488                 if( y0 != y1 || y1 != y2 || y2 != y3 ) {
489 // bezier definite integral t0..t1
490                         double f4 = -y0/4 + 3*y1/4 - 3*y2/4 + y3/4;
491                         double f3 = y0 - 2*y1 + y2;
492                         double f2 = -3*y0/2 + 3*y1/2;
493                         double f1 = y0, t = t0;
494                         double t2 = t*t, t3 = t2*t, t4 = t3*t;
495                         t0 = t4*f4 + t3*f3 + t2*f2 + t*f1;
496                         t = t1;  t2 = t*t;  t3 = t2*t;  t4 = t3*t;
497                         t1 = t4*f4 + t3*f3 + t2*f2 + t*f1;
498                 }
499                 else {
500                         t0 *= y0;  t1 *= y0;
501                 }
502                 value += dt * (t1 - t0);
503         }
504
505         return value + 1e-6;
506 }
507
508 int64_t FloatAutos::speed_position(double pos)
509 {
510         double length = track->get_length();
511         int64_t l = -1, r = track->to_units(length, 1);
512         if( r < 1 ) r = 1;
513         for( int i=32; --i >= 0 && automation_integral(0,r,PLAY_FORWARD) <= pos; r*=2 );
514         for( int i=64; --i >= 0 && (r-l)>1; ) {
515                 int64_t m = (l + r) / 2;
516                 double t = automation_integral(0,m,PLAY_FORWARD) - pos;
517                 *(t >= 0 ? &r : &l) = m;
518         }
519         return r;
520 }
521