Blender V4.3
strip_retiming.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_math_base.hh"
10#include "MEM_guardedalloc.h"
11
12#include "DNA_scene_types.h"
13#include "DNA_sequence_types.h"
14
15#include "BLI_listbase.h"
16#include "BLI_map.hh"
17#include "BLI_math_geom.h"
18#include "BLI_math_vector.h"
19#include "BLI_span.hh"
20#include "BLI_vector.hh"
21
22#include "BKE_sound.h"
23
24#include "SEQ_iterator.hh"
25#include "SEQ_retiming.hh"
26#include "SEQ_sequencer.hh"
27#include "SEQ_time.hh"
28#include "SEQ_transform.hh"
29
30#include "sequencer.hh"
31#include "strip_time.hh"
32
34
40
42{
43 return SEQ_retiming_key_index_get(seq, key) == seq->retiming_keys_num - 1;
44}
45
50
52{
53 return key - seq->retiming_keys;
54}
55
57 const Sequence *seq,
58 const int timeline_frame)
59{
60 for (auto &key : SEQ_retiming_keys_get(seq)) {
61 const int key_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, &key);
62 if (key_timeline_frame == timeline_frame) {
63 return &key;
64 }
65 }
66
67 return nullptr;
68}
69
71{
72 SeqRetimingKey *start_key = nullptr;
73 for (auto &key : SEQ_retiming_keys_get(seq)) {
74 if (SEQ_retiming_is_last_key(seq, &key)) {
75 break;
76 }
77 if (key.strip_frame_index > frame_index) {
78 break;
79 }
80
81 start_key = &key;
82 }
83
84 return start_key;
85}
86
88{
89 return seq->retiming_keys_num;
90}
91
93{
94 if (!SEQ_retiming_is_allowed(seq)) {
95 return;
96 }
97
98 if (SEQ_retiming_is_active(seq)) {
99 return;
100 }
101
102 seq->retiming_keys = (SeqRetimingKey *)MEM_calloc_arrayN(2, sizeof(SeqRetimingKey), __func__);
103 SeqRetimingKey *key = seq->retiming_keys + 1;
104 key->strip_frame_index = seq->len - 1;
105 key->retiming_factor = 1.0f;
106 seq->retiming_keys_num = 2;
107}
108
110{
111 if (seq->retiming_keys != nullptr) {
113 seq->retiming_keys = nullptr;
114 seq->retiming_keys_num = 0;
115 }
116 seq->flag &= ~SEQ_SHOW_RETIMING;
117}
118
119static void retiming_key_overlap(Scene *scene, Sequence *seq)
120{
124 dependant.add(seq);
125 SEQ_iterator_set_expand(scene, seqbase, dependant, SEQ_query_strip_effect_chain);
126 strips.add_multiple(dependant);
127 dependant.remove(seq);
128 SEQ_transform_handle_overlap(scene, seqbase, strips, dependant, true);
129}
130
132{
133 if (!SEQ_retiming_is_allowed(seq)) {
134 return;
135 }
136
138
142
143 retiming_key_overlap(scene, seq);
144}
145
147{
148 return seq->retiming_keys_num > 1;
149}
150
152{
153 return seq->flag & SEQ_SHOW_RETIMING;
154}
155
167
169{
170 const SeqRetimingKey *end_key = start_key + 1;
171 return end_key->strip_frame_index - start_key->strip_frame_index;
172}
173
174static float seq_retiming_segment_step_get(const SeqRetimingKey *start_key)
175{
176 const SeqRetimingKey *end_key = start_key + 1;
177 const int segment_length = seq_retiming_segment_length_get(start_key);
178 const float segment_fac_diff = end_key->retiming_factor - start_key->retiming_factor;
179 return segment_fac_diff / segment_length;
180}
181
183 double r_v1[2],
184 double r_v2[2])
185{
186 const SeqRetimingKey *end_key = start_key + 1;
187 r_v1[0] = start_key->strip_frame_index;
188 r_v1[1] = start_key->retiming_factor;
189 r_v2[0] = end_key->strip_frame_index;
190 r_v2[1] = end_key->retiming_factor;
191}
192
194 double r_center[2],
195 double *radius)
196{
197 double s1_1[2], s1_2[2], s2_1[2], s2_2[2], p1_2[2];
198
199 /* Get 2 segments. */
200 seq_retiming_segment_as_line_segment(start_key - 1, s1_1, s1_2);
201 seq_retiming_segment_as_line_segment(start_key + 1, s2_1, s2_2);
202 /* Backup first segment end point - needed to calculate arc radius. */
203 copy_v2_v2_db(p1_2, s1_2);
204 /* Convert segments to vectors. */
205 double v1[2], v2[2];
206 sub_v2_v2v2_db(v1, s1_1, s1_2);
207 sub_v2_v2v2_db(v2, s2_1, s2_2);
208 /* Rotate segments by 90 degrees around seg. 1 end and seg. 2 start point. */
209 std::swap(v1[0], v1[1]);
210 std::swap(v2[0], v2[1]);
211 v1[0] *= -1;
212 v2[0] *= -1;
213 copy_v2_v2_db(s1_1, s1_2);
214 add_v2_v2_db(s1_2, v1);
215 copy_v2_v2_db(s2_2, s2_1);
216 add_v2_v2_db(s2_2, v2);
217 /* Get center and radius of arc segment between 2 linear segments. */
218 double lambda, mu;
219 isect_seg_seg_v2_lambda_mu_db(s1_1, s1_2, s2_1, s2_2, &lambda, &mu);
220 r_center[0] = s1_1[0] + lambda * (s1_2[0] - s1_1[0]);
221 r_center[1] = s1_1[1] + lambda * (s1_2[1] - s1_1[1]);
222 *radius = len_v2v2_db(p1_2, r_center);
223}
224
226{
227 return (key->flag & SEQ_SPEED_TRANSITION_IN) != 0 || (key->flag & SEQ_SPEED_TRANSITION_OUT) != 0;
228}
229
231{
232 return (key->flag & SEQ_SPEED_TRANSITION_IN) != 0;
233}
234
236{
237 if (key->flag & SEQ_SPEED_TRANSITION_OUT) {
238 return key - 1;
239 }
240 if (key->flag & SEQ_SPEED_TRANSITION_IN) {
241 return key;
242 }
243 return nullptr;
244}
245
247{
248 return (key->flag & SEQ_FREEZE_FRAME_IN) != 0 || (key->flag & SEQ_FREEZE_FRAME_OUT) != 0;
249}
250
251/* Check colinearity of 2 segments allowing for some imprecision.
252 * `isect_seg_seg_v2_lambda_mu_db()` return value does not work well in this case. */
253
255{
256 const float prev_speed = SEQ_retiming_key_speed_get(seq, key - 1);
257 const float next_speed = SEQ_retiming_key_speed_get(seq, key + 2);
258
259 return abs(prev_speed - next_speed) < 0.01f;
260}
261
262static float seq_retiming_evaluate_arc_segment(const SeqRetimingKey *key, const float frame_index)
263{
264 double c[2], r;
266 const int side = c[1] > key->retiming_factor ? -1 : 1;
267 const float y = c[1] + side * sqrt(pow(r, 2) - pow((frame_index - c[0]), 2));
268 return y;
269}
270
271float seq_retiming_evaluate(const Sequence *seq, const float frame_index)
272{
273 const SeqRetimingKey *start_key = SEQ_retiming_find_segment_start_key(seq, frame_index);
274
275 const int start_key_index = start_key - seq->retiming_keys;
276 BLI_assert(start_key_index < seq->retiming_keys_num);
277
278 const float segment_frame_index = frame_index - start_key->strip_frame_index;
279
280 if (!SEQ_retiming_key_is_transition_start(start_key)) {
281 const float segment_step = seq_retiming_segment_step_get(start_key);
282 return std::min(1.0f, start_key->retiming_factor + segment_step * segment_frame_index);
283 }
284
285 if (seq_retiming_transition_is_linear(seq, start_key)) {
286 const float segment_step = seq_retiming_segment_step_get(start_key - 1);
287 return std::min(1.0f, start_key->retiming_factor + segment_step * segment_frame_index);
288 }
289
290 /* Sanity check for transition type. */
291 BLI_assert(start_key_index > 0);
292 BLI_assert(start_key_index < seq->retiming_keys_num - 1);
293 UNUSED_VARS_NDEBUG(start_key_index);
294
295 return std::min(1.0f, seq_retiming_evaluate_arc_segment(start_key, frame_index));
296}
297
298static SeqRetimingKey *seq_retiming_add_key(const Scene * /*scene*/,
299 Sequence *seq,
300 float frame_index)
301{
302 /* Clamp timeline frame to strip content range. */
303 if (frame_index <= 0) {
304 return &seq->retiming_keys[0];
305 }
306 if (frame_index >= SEQ_retiming_last_key_get(seq)->strip_frame_index) {
307 return SEQ_retiming_last_key_get(seq); /* This is expected for strips with no offsets. */
308 }
309
310 SeqRetimingKey *start_key = SEQ_retiming_find_segment_start_key(seq, frame_index);
311
312 if (start_key->strip_frame_index == frame_index) {
313 return start_key; /* Retiming key already exists. */
314 }
315
316 if ((start_key->flag & SEQ_SPEED_TRANSITION_IN) != 0 ||
317 (start_key->flag & SEQ_FREEZE_FRAME_IN) != 0)
318 {
319 return nullptr;
320 }
321
322 float value = seq_retiming_evaluate(seq, frame_index);
323
324 SeqRetimingKey *keys = seq->retiming_keys;
325 size_t keys_count = SEQ_retiming_keys_count(seq);
326 const int new_key_index = start_key - keys + 1;
327 BLI_assert(new_key_index >= 0);
328 BLI_assert(new_key_index < keys_count);
329
331 (keys_count + 1) * sizeof(SeqRetimingKey), __func__);
332 if (new_key_index > 0) {
333 memcpy(new_keys, keys, new_key_index * sizeof(SeqRetimingKey));
334 }
335 if (new_key_index < keys_count) {
336 memcpy(new_keys + new_key_index + 1,
337 keys + new_key_index,
338 (keys_count - new_key_index) * sizeof(SeqRetimingKey));
339 }
340 MEM_freeN(keys);
341 seq->retiming_keys = new_keys;
342 seq->retiming_keys_num++;
343
344 SeqRetimingKey *added_key = (new_keys + new_key_index);
345 added_key->strip_frame_index = frame_index;
346 added_key->retiming_factor = value;
347
348 return added_key;
349}
350
351SeqRetimingKey *SEQ_retiming_add_key(const Scene *scene, Sequence *seq, const int timeline_frame)
352{
353 const int sound_offset = SEQ_time_get_rounded_sound_offset(scene, seq);
354 const float frame_index = (timeline_frame - SEQ_time_start_frame_get(seq) - sound_offset) *
356
357 return seq_retiming_add_key(scene, seq, frame_index);
358}
359
361 const Sequence *seq,
362 SeqRetimingKey *key,
363 const int timeline_frame)
364{
366 SeqRetimingKey *key_end = key_start + 1;
367 const int start_frame_index = key_start->strip_frame_index;
368 const int midpoint = key_start->original_strip_frame_index;
369 const int new_frame_index = (timeline_frame - SEQ_time_start_frame_get(seq)) *
371 int new_midpoint_offset = new_frame_index - midpoint;
372 const float prev_segment_step = seq_retiming_segment_step_get(key_start - 1);
373 const float next_segment_step = seq_retiming_segment_step_get(key_end);
374
375 /* Prevent keys crossing eachother. */
376 SeqRetimingKey *prev_segment_end = key_start - 1, *next_segment_start = key_end + 1;
377 const int offset_max_left = midpoint - prev_segment_end->strip_frame_index - 1;
378 const int offset_max_right = next_segment_start->strip_frame_index - midpoint - 1;
379 new_midpoint_offset = abs(new_midpoint_offset);
380 new_midpoint_offset = min_iii(new_midpoint_offset, offset_max_left, offset_max_right);
381 new_midpoint_offset = max_ii(new_midpoint_offset, 1);
382
383 key_start->strip_frame_index = midpoint - new_midpoint_offset;
384 key_end->strip_frame_index = midpoint + new_midpoint_offset;
385
386 const int offset = key_start->strip_frame_index - start_frame_index;
387 key_start->retiming_factor += offset * prev_segment_step;
388 key_end->retiming_factor -= offset * next_segment_step;
389}
390
392{
393 if ((key->flag & SEQ_FREEZE_FRAME_IN) != 0) {
394 SeqRetimingKey *next_key = key + 1;
395 key->flag &= ~SEQ_FREEZE_FRAME_IN;
396 next_key->flag &= ~SEQ_FREEZE_FRAME_OUT;
397 }
398 if ((key->flag & SEQ_FREEZE_FRAME_OUT) != 0) {
399 SeqRetimingKey *previous_key = key - 1;
400 key->flag &= ~SEQ_FREEZE_FRAME_OUT;
401 previous_key->flag &= ~SEQ_FREEZE_FRAME_IN;
402 }
403}
404
406 blender::Vector<SeqRetimingKey *> &keys_to_remove)
407{
408 /* Transitions need special treatment, so separate these from `keys_to_remove`. */
410
411 /* Cleanup freeze frames and extract transition keys. */
412 for (SeqRetimingKey *key : keys_to_remove) {
415 }
416 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0) {
417 transitions.append_non_duplicates(key);
418 transitions.append_non_duplicates(key + 1);
419 }
420 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
421 transitions.append_non_duplicates(key);
422 transitions.append_non_duplicates(key - 1);
423 }
424 }
425
426 /* Sanitize keys to be removed. */
427 keys_to_remove.remove_if([&](const SeqRetimingKey *key) {
428 return key->strip_frame_index == 0 || SEQ_retiming_is_last_key(seq, key) ||
430 });
431
432 const size_t keys_count = SEQ_retiming_keys_count(seq);
433 size_t new_keys_count = keys_count - keys_to_remove.size() - transitions.size() / 2;
434 SeqRetimingKey *new_keys = (SeqRetimingKey *)MEM_callocN(new_keys_count * sizeof(SeqRetimingKey),
435 __func__);
436 int keys_copied = 0;
437
438 /* Copy keys to new array. */
439 for (SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
440 /* Create key that was used to make transition in new array. */
441 if (transitions.contains(&key) && SEQ_retiming_key_is_transition_start(&key)) {
442 SeqRetimingKey *new_key = new_keys + keys_copied;
445 keys_copied++;
446 continue;
447 }
448 if (keys_to_remove.contains(&key) || transitions.contains(&key)) {
449 continue;
450 }
451 memcpy(new_keys + keys_copied, &key, sizeof(SeqRetimingKey));
452 keys_copied++;
453 }
454
456 seq->retiming_keys = new_keys;
457 seq->retiming_keys_num = new_keys_count;
458}
459
461{
462 if (key->strip_frame_index == 0 || SEQ_retiming_is_last_key(seq, key)) {
463 return; /* First and last key can not be removed. */
464 }
465
468 }
469
470 size_t keys_count = SEQ_retiming_keys_count(seq);
471 SeqRetimingKey *keys = (SeqRetimingKey *)MEM_callocN((keys_count - 1) * sizeof(SeqRetimingKey),
472 __func__);
473
474 const int key_index = key - seq->retiming_keys;
475 memcpy(keys, seq->retiming_keys, (key_index) * sizeof(SeqRetimingKey));
476 memcpy(keys + key_index,
477 seq->retiming_keys + key_index + 1,
478 (keys_count - key_index - 1) * sizeof(SeqRetimingKey));
480 seq->retiming_keys = keys;
481 seq->retiming_keys_num--;
482}
483
484/* This function removes transition segment and creates retiming key where it originally was. */
486 Sequence *seq,
487 SeqRetimingKey *key)
488{
489 SeqRetimingKey *transition_start = key;
490 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
491 transition_start = key - 1;
492 }
493
494 const float orig_frame_index = transition_start->original_strip_frame_index;
495 const float orig_retiming_factor = transition_start->original_retiming_factor;
496
497 /* Remove both keys defining transition. */
498 int key_index = SEQ_retiming_key_index_get(seq, transition_start);
499 seq_retiming_remove_key_ex(seq, transition_start);
500 seq_retiming_remove_key_ex(seq, seq->retiming_keys + key_index);
501
502 /* Create original linear key. */
503 SeqRetimingKey *orig_key = seq_retiming_add_key(scene, seq, orig_frame_index);
504 orig_key->retiming_factor = orig_retiming_factor;
505 return orig_key;
506}
507
509{
510
512 seq_retiming_remove_transition(scene, seq, key);
513 return;
514 }
515
517}
518
519static float seq_retiming_clamp_create_offset(SeqRetimingKey *key, float offset)
520{
521 SeqRetimingKey *prev_key = key - 1;
522 SeqRetimingKey *next_key = key + 1;
523 const float prev_dist = key->strip_frame_index - prev_key->strip_frame_index;
524 const float next_dist = next_key->strip_frame_index - key->strip_frame_index;
525 return min_fff(offset, prev_dist - 1, next_dist - 1);
526}
527
529 Sequence *seq,
530 SeqRetimingKey *key,
531 const int offset)
532{
533 /* First offset old key, then add new key to original place with same fac
534 * This is not great way to do things, but it's done in order to be able to freeze last key. */
536 return nullptr;
537 }
538
539 int clamped_offset = seq_retiming_clamp_create_offset(
540 key, offset * SEQ_time_media_playback_rate_factor_get(scene, seq));
541
542 const int orig_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
543 const float orig_retiming_factor = key->retiming_factor;
544 key->strip_frame_index += clamped_offset;
546
547 SeqRetimingKey *new_key = SEQ_retiming_add_key(scene, seq, orig_timeline_frame);
548
549 if (new_key == nullptr) {
550 key->strip_frame_index -= clamped_offset;
551 key->flag &= ~SEQ_FREEZE_FRAME_OUT;
552 return nullptr;
553 }
554
555 new_key->retiming_factor = orig_retiming_factor;
556 new_key->flag |= SEQ_FREEZE_FRAME_IN;
557
558 /* Tag previous key as freeze frame key. This is only a convenient way to prevent creating
559 * speed transitions. When freeze frame is deleted, this flag should be cleared. */
560 return new_key + 1;
561}
562
564 Sequence *seq,
565 SeqRetimingKey *key,
566 float offset)
567{
569 BLI_assert(key->strip_frame_index != 0);
570
571 SeqRetimingKey *prev_key = key - 1;
572 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0 ||
573 (prev_key->flag & SEQ_SPEED_TRANSITION_IN) != 0)
574 {
575 return nullptr;
576 }
577
578 if ((key->flag & SEQ_FREEZE_FRAME_IN) != 0 || (prev_key->flag & SEQ_FREEZE_FRAME_IN) != 0) {
579 return nullptr;
580 }
581
582 float clamped_offset = seq_retiming_clamp_create_offset(key, offset);
583
584 const int orig_key_index = SEQ_retiming_key_index_get(seq, key);
585 const int orig_frame_index = key->strip_frame_index;
586 const float orig_retiming_factor = key->retiming_factor;
587
588 SeqRetimingKey *transition_out = seq_retiming_add_key(
589 scene, seq, orig_frame_index + clamped_offset);
590 transition_out->flag |= SEQ_SPEED_TRANSITION_OUT;
591
592 SeqRetimingKey *transition_in = seq_retiming_add_key(
593 scene, seq, orig_frame_index - clamped_offset);
594 transition_in->flag |= SEQ_SPEED_TRANSITION_IN;
595 transition_in->original_strip_frame_index = orig_frame_index;
596 transition_in->original_retiming_factor = orig_retiming_factor;
597
598 seq_retiming_remove_key_ex(seq, seq->retiming_keys + orig_key_index + 1);
599 return seq->retiming_keys + orig_key_index + 1;
600}
601
603{
604 if (key->strip_frame_index == 0) {
605 return 1.0f;
606 }
607
608 const SeqRetimingKey *key_prev = key - 1;
609
610 const int frame_index_max = seq->len - 1;
611 const int frame_retimed_prev = round_fl_to_int(key_prev->retiming_factor * frame_index_max);
612 const int frame_index_prev = key_prev->strip_frame_index;
613 const int frame_retimed = round_fl_to_int(key->retiming_factor * frame_index_max);
614 const int frame_index = key->strip_frame_index;
615
616 const int fragment_length_retimed = frame_retimed - frame_retimed_prev;
617 const int fragment_length_original = frame_index - frame_index_prev;
618
619 const float speed = float(fragment_length_retimed) / float(fragment_length_original);
620 return speed;
621}
622
626};
627
635
637 public:
638 int start, end;
639 float speed;
641
643 RetimingRange(const Sequence *seq, int start_frame, int end_frame, float speed, eRangeType type)
644 : start(start_frame), end(end_frame), speed(speed), type(type)
645 {
646 if (type == TRANSITION) {
647 this->speed = 1.0f;
649 }
650 }
651
652 RetimingRange(int start_frame, int end_frame, float speed, eRangeType type)
653 : start(start_frame), end(end_frame), speed(speed), type(type)
654 {
655 }
656
658 {
659 RetimingRange new_range = RetimingRange(start, end, speed, type);
660 for (int i = 0; i < speed_table.size(); i++) {
661 new_range.speed_table.append(speed_table[i]);
662 }
663 return new_range;
664 }
665
666 /* Create new range representing overlap of 2 ranges.
667 * Returns overlapping range. */
669 {
670 RetimingRange new_range = RetimingRange(0, 0, 0, LINEAR);
671
672 /* Offsets to merge speed tables. */
673 int range_offset = 0, rhs_range_offset = 0;
674 if (intersect_type(rhs_range) == FULL) {
675 new_range.start = start;
676 new_range.end = end;
677 rhs_range_offset = start - rhs_range.start;
678 }
679 else if (intersect_type(rhs_range) == PARTIAL_START) {
680 new_range.start = start;
681 new_range.end = rhs_range.end;
682 rhs_range_offset = start - rhs_range.start;
683 }
684 else if (intersect_type(rhs_range) == PARTIAL_END) {
685 new_range.start = rhs_range.start;
686 new_range.end = end;
687 range_offset = rhs_range.start - start;
688 }
689 else if (intersect_type(rhs_range) == INSIDE) {
690 new_range.start = rhs_range.start;
691 new_range.end = rhs_range.end;
692 range_offset = rhs_range.start - start;
693 }
694
695 if (type != TRANSITION && rhs_range.type != TRANSITION) {
696 new_range.speed = speed * rhs_range.speed;
697 return new_range;
698 }
699
700 /* One of ranges is transition type, so speed tables has to be copied. */
701 new_range.type = TRANSITION;
702 new_range.speed = 1.0f;
703 const int new_range_len = new_range.end - new_range.start;
704
705 if (type == TRANSITION && rhs_range.type == TRANSITION) {
706 for (int i = 0; i < new_range_len; i++) {
707 const float range_speed = speed_table[i + range_offset];
708 const float rhs_range_speed = rhs_range.speed_table[i + rhs_range_offset];
709 new_range.speed_table.append(range_speed * rhs_range_speed);
710 }
711 }
712 else if (type == TRANSITION) {
713 for (int i = 0; i < new_range_len; i++) {
714 const float range_speed = speed_table[i + range_offset];
715 new_range.speed_table.append(range_speed * rhs_range.speed);
716 }
717 }
718 else if (rhs_range.type == TRANSITION) {
719 for (int i = 0; i < new_range_len; i++) {
720 const float rhs_range_speed = rhs_range.speed_table[i + rhs_range_offset];
721 new_range.speed_table.append(speed * rhs_range_speed);
722 }
723 }
724
725 return new_range;
726 }
727
729 {
730 for (int timeline_frame = start; timeline_frame <= end; timeline_frame++) {
731 /* We need number actual number of frames here. */
732 const double normal_step = 1 / double(seq->len);
733
734 const int frame_index = timeline_frame - SEQ_time_start_frame_get(seq);
735 /* Who needs calculus, when you can have slow code? */
736 const double val_prev = seq_retiming_evaluate(seq, frame_index - 1);
737 const double val = seq_retiming_evaluate(seq, frame_index);
738 const double speed_at_frame = (val - val_prev) / normal_step;
739 speed_table.append(speed_at_frame);
740 }
741 }
742
744 {
745 if (other.start <= start && other.end >= end) {
746 return FULL;
747 }
748 if (other.start > start && other.start < end && other.end > start && other.end < end) {
749 return INSIDE;
750 }
751 if (other.start > start && other.start < end) {
752 return PARTIAL_END;
753 }
754 if (other.end > start && other.end < end) {
755 return PARTIAL_START;
756 }
757 return NONE;
758 }
759};
760
762 public:
765 {
766 for (const SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
767 if (key.strip_frame_index == 0) {
768 continue;
769 }
770 const SeqRetimingKey *key_prev = &key - 1;
771 float speed = SEQ_retiming_key_speed_get(seq, &key);
772 int frame_start = SEQ_time_start_frame_get(seq) + key_prev->strip_frame_index;
773 int frame_end = SEQ_time_start_frame_get(seq) + key.strip_frame_index;
774
776 RetimingRange range = RetimingRange(seq, frame_start, frame_end, speed, type);
777 ranges.append(range);
778 }
779 }
780
782 {
783 if (ranges.is_empty()) {
784 for (const RetimingRange &rhs_range : rhs.ranges) {
786 rhs_range.start, rhs_range.end, rhs_range.speed, rhs_range.type);
787 ranges.append(range);
788 }
789 return *this;
790 }
791
792 for (int i = 0; i < ranges.size(); i++) {
793 RetimingRange &range = ranges[i];
794 for (const RetimingRange &rhs_range : rhs.ranges) {
795 if (range.intersect_type(rhs_range) == NONE) {
796 continue;
797 }
798 else if (range.intersect_type(rhs_range) == FULL) {
799 RetimingRange isect = range * rhs_range;
800 ranges.remove(i);
801 ranges.insert(i, isect);
802 }
803 if (range.intersect_type(rhs_range) == PARTIAL_START) {
804 ranges.insert(i, range * rhs_range);
805 ranges.insert(i, range * rhs_range);
806 range.start = rhs_range.end + 1;
807 }
808 else if (range.intersect_type(rhs_range) == PARTIAL_END) {
809 ranges.insert(i, range * rhs_range);
810 range.end = rhs_range.start;
811 }
812 else if (range.intersect_type(rhs_range) == INSIDE) {
813 RetimingRange left_range = range.duplicate();
814 left_range.end = rhs_range.start;
815 range.start = rhs_range.end + 1;
816
817 ranges.insert(i, left_range);
818 ranges.insert(i, range * rhs_range);
819 }
820 }
821 }
822 return *this;
823 }
824};
825
827{
828 RetimingRangeData strip_retiming_data = RetimingRangeData(seq);
829
830 const Sequence *meta_parent = seq_sequence_lookup_meta_by_seq(scene, seq);
831 if (meta_parent == nullptr) {
832 return strip_retiming_data;
833 }
834
835 RetimingRangeData meta_retiming_data = RetimingRangeData(meta_parent);
836 strip_retiming_data *= meta_retiming_data;
837 return strip_retiming_data;
838}
839
841{
842 /* Content cut off by `anim_startofs` is as if it does not exist for sequencer. But Audaspace
843 * seeking relies on having animation buffer initialized for whole sequence. */
844 if (seq->anim_startofs > 0) {
845 const int seq_start = SEQ_time_start_frame_get(seq);
847 seq->scene_sound, seq_start - seq->anim_startofs, seq_start, 1.0f);
848 }
849
850 int sound_offset = SEQ_time_get_rounded_sound_offset(scene, seq);
851
852 RetimingRangeData retiming_data = seq_retiming_range_data_get(scene, seq);
853 for (int i = 0; i < retiming_data.ranges.size(); i++) {
854 RetimingRange range = retiming_data.ranges[i];
855 if (range.type == TRANSITION) {
856
857 const int range_length = range.end - range.start;
858 for (int i = 0; i <= range_length; i++) {
859 const int frame = range.start + i;
861 seq->scene_sound, frame + sound_offset, range.speed_table[i], true);
862 }
863 }
864 else {
866 seq->scene_sound, range.start + sound_offset, range.end + sound_offset, range.speed);
867 }
868 }
869}
870
872 const Sequence *seq,
873 const SeqRetimingKey *key)
874{
875 int sound_offset = SEQ_time_get_rounded_sound_offset(scene, seq);
876 return round_fl_to_int(SEQ_time_start_frame_get(seq) + sound_offset +
877 key->strip_frame_index /
879}
880
881static int seq_retiming_clamp_transition_offset(SeqRetimingKey *start_key, int offset)
882{
883 SeqRetimingKey *end_key = start_key + 1;
884 SeqRetimingKey *prev_key = start_key - 1;
885 SeqRetimingKey *next_key = start_key + 2;
886 const int prev_dist = start_key->strip_frame_index - prev_key->strip_frame_index;
887 const int next_dist = next_key->strip_frame_index - end_key->strip_frame_index;
888
889 if (offset >= 0) {
890 return min_ii(offset, next_dist - 1);
891 }
892 else {
893 return max_ii(offset, -(prev_dist - 1));
894 }
895}
896
897static void seq_retiming_transition_offset(const Scene *scene,
898 Sequence *seq,
899 SeqRetimingKey *key,
900 const int offset)
901{
902 int clamped_offset = seq_retiming_clamp_transition_offset(key, offset);
903 const float duration = (key->original_strip_frame_index - key->strip_frame_index);
904 const bool was_selected = SEQ_retiming_selection_contains(SEQ_editing_get(scene), key);
905
906 SeqRetimingKey *original_key = seq_retiming_remove_transition(scene, seq, key);
907 original_key->strip_frame_index += clamped_offset *
909
910 SeqRetimingKey *transition_out = SEQ_retiming_add_transition(scene, seq, original_key, duration);
911
912 if (was_selected) {
913 SEQ_retiming_selection_append(transition_out);
914 SEQ_retiming_selection_append(transition_out - 1);
915 }
916}
917
919 Sequence *seq,
920 SeqRetimingKey *key,
921 const int timeline_frame)
922{
923 int prev_key_timeline_frame = -MAXFRAME;
924 int next_key_timeline_frame = MAXFRAME;
925
926 if (key->strip_frame_index > 0) {
927 SeqRetimingKey *prev_key = key - 1;
928 prev_key_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, prev_key);
929 }
930
931 if (!SEQ_retiming_is_last_key(seq, key)) {
932 SeqRetimingKey *next_key = key + 1;
933 next_key_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, next_key);
934 }
935
936 const int orig_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
937 int clamped_timeline_frame = timeline_frame;
938
939 if (timeline_frame < orig_timeline_frame) {
940 clamped_timeline_frame = max_ii(timeline_frame, prev_key_timeline_frame + 1);
941 }
942 else if (timeline_frame > orig_timeline_frame) {
943 clamped_timeline_frame = min_ii(timeline_frame, next_key_timeline_frame - 1);
944 }
945 return clamped_timeline_frame;
946}
947
948/* Remove and re-create transition. This way transition won't change length.
949 * Alternative solution is to find where in arc segment the `y` value is closest to key
950 * retiming factor, then trim transition to that point. This would change transition length. */
951
952static void seq_retiming_fix_transition(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
953{
954 const int keys_num = seq->retiming_keys_num;
955
956 const float transition_duration = (key->original_strip_frame_index - key->strip_frame_index);
957 SeqRetimingKey *orig_key = seq_retiming_remove_transition(scene, seq, key);
958 SEQ_retiming_add_transition(scene, seq, orig_key, transition_duration);
959 BLI_assert(keys_num == seq->retiming_keys_num);
960 UNUSED_VARS_NDEBUG(keys_num);
961}
962
963static void seq_retiming_fix_transitions(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
964{
965 if (SEQ_retiming_key_index_get(seq, key) <= 1) {
966 return;
967 }
968
969 const int key_index = SEQ_retiming_key_index_get(seq, key);
970
971 /* Store value, since handles array will be reallocated. */
972 bool is_last_key = SEQ_retiming_is_last_key(seq, key);
973
974 SeqRetimingKey *prev_key = key - 2;
976 seq_retiming_fix_transition(scene, seq, prev_key);
977 }
978
979 if (is_last_key) {
980 return;
981 }
982
983 SeqRetimingKey *next_key = &SEQ_retiming_keys_get(seq)[key_index + 1];
985 seq_retiming_fix_transition(scene, seq, next_key);
986 }
987}
988
989static void seq_retiming_key_offset(const Scene *scene,
990 Sequence *seq,
991 SeqRetimingKey *key,
992 const int offset)
993{
994 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0) {
995 seq_retiming_transition_offset(scene, seq, key, offset);
996 }
997 else {
999 seq_retiming_fix_transitions(scene, seq, key);
1000 }
1001}
1002
1004 Sequence *seq,
1005 SeqRetimingKey *key,
1006 const int timeline_frame)
1007{
1008 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
1009 return;
1010 }
1011
1012 const int orig_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
1013 const int clamped_timeline_frame = seq_retiming_clamp_timeline_frame(
1014 scene, seq, key, timeline_frame);
1015 const int offset = clamped_timeline_frame - orig_timeline_frame;
1016
1017 const int key_count = SEQ_retiming_keys_get(seq).size();
1018 const int key_index = SEQ_retiming_key_index_get(seq, key);
1019
1020 if (orig_timeline_frame == SEQ_time_right_handle_frame_get(scene, seq)) {
1021 for (int i = key_index; i < key_count; i++) {
1022 SeqRetimingKey *key_iter = &SEQ_retiming_keys_get(seq)[i];
1023 seq_retiming_key_offset(scene, seq, key_iter, offset);
1024 }
1025 }
1026 else if (orig_timeline_frame == SEQ_time_left_handle_frame_get(scene, seq) ||
1027 key->strip_frame_index == 0)
1028 {
1029 seq->start += offset;
1030 for (int i = key_index + 1; i < key_count; i++) {
1031 SeqRetimingKey *key_iter = &SEQ_retiming_keys_get(seq)[i];
1032 seq_retiming_key_offset(scene, seq, key_iter, -offset);
1033 }
1034 }
1035 else {
1036 seq_retiming_key_offset(scene, seq, key, offset);
1037 }
1038
1042}
1043
1045 const Scene *scene, Sequence *seq, SeqRetimingKey *key, const float speed, bool keep_retiming)
1046{
1047 if (key->strip_frame_index == 0) {
1048 return;
1049 }
1050
1051 const SeqRetimingKey *key_prev = key - 1;
1052 const float speed_fac = 100.0f / speed;
1053
1054 const int frame_index_max = seq->len;
1055 const int frame_retimed_prev = round_fl_to_int(key_prev->retiming_factor * frame_index_max);
1056 const int frame_retimed = round_fl_to_int(key->retiming_factor * frame_index_max);
1057
1058 const int segment_duration = (frame_retimed - frame_retimed_prev) /
1060 const int new_duration = segment_duration * speed_fac;
1061
1062 const int orig_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key);
1063 const int new_timeline_frame = SEQ_retiming_key_timeline_frame_get(scene, seq, key_prev) +
1064 new_duration;
1065
1066 SEQ_retiming_key_timeline_frame_set(scene, seq, key, new_timeline_frame);
1067
1068 if (keep_retiming) {
1069 const int key_index = SEQ_retiming_key_index_get(seq, key);
1070 const int offset = new_timeline_frame - orig_timeline_frame;
1071 for (int i = key_index + 1; i < SEQ_retiming_keys_count(seq); i++) {
1072 SeqRetimingKey *key_iter = &SEQ_retiming_keys_get(seq)[i];
1073 seq_retiming_key_offset(scene, seq, key_iter, offset);
1074 }
1075 }
1076}
1077
1079{
1080 bool was_empty = true;
1081
1082 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1083 for (SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
1084 was_empty &= (key.flag & SEQ_KEY_SELECTED) == 0;
1085 key.flag &= ~SEQ_KEY_SELECTED;
1086 }
1087 }
1088 return !was_empty;
1089}
1090
1092
1093 SeqRetimingKey *key)
1094{
1095 key->flag |= SEQ_KEY_SELECTED;
1096}
1097
1099{
1100 key->flag &= ~SEQ_KEY_SELECTED;
1101}
1102
1104{
1106 dst->flag |= (src->flag & SEQ_KEY_SELECTED);
1107}
1108
1110{
1112 if (!ed) {
1113 return selection;
1114 }
1115 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1116 for (SeqRetimingKey &key : SEQ_retiming_keys_get(seq)) {
1117 if ((key.flag & SEQ_KEY_SELECTED) != 0) {
1118 selection.add(&key, seq);
1119 }
1120 }
1121 }
1122 return selection;
1123}
1124
1126{
1127 LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
1128 for (const SeqRetimingKey &key_iter : SEQ_retiming_keys_get(seq)) {
1129 if ((key_iter.flag & SEQ_KEY_SELECTED) != 0 && &key_iter == key) {
1130 return true;
1131 }
1132 }
1133 }
1134 return false;
1135}
1136
1138{
1140 SeqRetimingKey *key_end = key_start + 1;
1141 bool has_start = false, has_end = false;
1142
1144
1145 for (auto item : selection.items()) {
1146 if (item.key == key_start) {
1147 has_start = true;
1148 }
1149 if (item.key == key_end) {
1150 has_end = true;
1151 }
1152 if (has_start && has_end) {
1153 return true;
1154 }
1155 }
1156 return false;
1157}
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle, int frame_start, int frame_end, float pitch)
void BKE_sound_set_scene_sound_pitch_at_frame(void *handle, int frame, float pitch, char animated)
#define BLI_assert(a)
Definition BLI_assert.h:50
sqrt(x)+1/max(0
#define LISTBASE_FOREACH(type, var, list)
MINLINE int round_fl_to_int(float a)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
MINLINE int min_iii(int a, int b, int c)
MINLINE float min_fff(float a, float b, float c)
int isect_seg_seg_v2_lambda_mu_db(const double v1[2], const double v2[2], const double v3[2], const double v4[2], double *r_lambda, double *r_mu)
MINLINE void copy_v2_v2_db(double r[2], const double a[2])
MINLINE double len_v2v2_db(const double v1[2], const double v2[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void add_v2_v2_db(double r[2], const double a[2])
MINLINE void sub_v2_v2v2_db(double r[2], const double a[2], const double b[2])
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
typedef double(DMatrix)[4][4]
#define MAXFRAME
@ SEQ_KEY_SELECTED
@ SEQ_SPEED_TRANSITION_OUT
@ SEQ_FREEZE_FRAME_OUT
@ SEQ_SPEED_TRANSITION_IN
@ SEQ_FREEZE_FRAME_IN
@ SEQ_TYPE_SOUND_RAM
@ SEQ_TYPE_META
@ SEQ_TYPE_SCENE
@ SEQ_TYPE_MOVIECLIP
@ SEQ_TYPE_IMAGE
@ SEQ_TYPE_MOVIE
@ SEQ_TYPE_MASK
struct SeqRetimingKey SeqRetimingKey
@ SEQ_SHOW_RETIMING
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
RetimingRangeData & operator*=(const RetimingRangeData &rhs)
blender::Vector< RetimingRange > ranges
RetimingRangeData(const Sequence *seq)
RetimingRange(const Sequence *seq, int start_frame, int end_frame, float speed, eRangeType type)
const eIntersectType intersect_type(const RetimingRange &other) const
RetimingRange operator*(const RetimingRange &rhs_range)
RetimingRange(int start_frame, int end_frame, float speed, eRangeType type)
void claculate_speed_table_from_seq(const Sequence *seq)
blender::Vector< float > speed_table
RetimingRange duplicate()
bool add(const Key &key)
void add_multiple(Span< Key > keys)
bool remove(const Key &key)
int64_t size() const
int64_t remove_if(Predicate &&predicate)
bool contains(const T &value) const
void append(const T &value)
pow(value.r - subtrahend, 2.0)") .do_static_compilation(true)
local_group_size(16, 16) .push_constant(Type rhs
draw_view in_light_buf[] float
void SEQ_iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Sequence * > &strips, void seq_query_func(const Scene *scene, Sequence *seq_reference, ListBase *seqbase, VectorSet< Sequence * > &strips))
Definition iterator.cc:61
void SEQ_query_strip_effect_chain(const Scene *scene, Sequence *reference_strip, ListBase *seqbase, VectorSet< Sequence * > &strips)
Definition iterator.cc:210
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:43
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
GPU_SHADER_INTERFACE_INFO(overlay_edit_curve_handle_iface, "vert").flat(Type pos vertex_in(1, Type::UINT, "data") .vertex_out(overlay_edit_curve_handle_iface) .geometry_layout(PrimitiveIn Frequency::GEOMETRY storage_buf(1, Qualifier::READ, "uint", "data[]", Frequency::GEOMETRY) .push_constant(Type Frequency::GEOMETRY selection[]
Sequence * seq_sequence_lookup_meta_by_seq(const Scene *scene, const Sequence *key)
blender::Span< Sequence * > seq_sequence_lookup_effects_by_seq(const Scene *scene, const Sequence *key)
ListBase * SEQ_active_seqbase_get(const Editing *ed)
Definition sequencer.cc:416
Editing * SEQ_editing_get(const Scene *scene)
Definition sequencer.cc:262
MutableSpan< SeqRetimingKey > SEQ_retiming_keys_get(const Sequence *seq)
int SEQ_retiming_keys_count(const Sequence *seq)
static RetimingRangeData seq_retiming_range_data_get(const Scene *scene, const Sequence *seq)
bool SEQ_retiming_data_is_editable(const Sequence *seq)
bool SEQ_retiming_is_active(const Sequence *seq)
void SEQ_retiming_remove_multiple_keys(Sequence *seq, blender::Vector< SeqRetimingKey * > &keys_to_remove)
bool SEQ_retiming_key_is_freeze_frame(const SeqRetimingKey *key)
static SeqRetimingKey * seq_retiming_remove_transition(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
eIntersectType
@ INSIDE
@ PARTIAL_START
@ NONE
@ FULL
@ PARTIAL_END
void SEQ_retiming_key_speed_set(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const float speed, bool keep_retiming)
static float seq_retiming_segment_step_get(const SeqRetimingKey *start_key)
static void seq_retiming_transition_offset(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const int offset)
static int seq_retiming_clamp_transition_offset(SeqRetimingKey *start_key, int offset)
bool SEQ_retiming_key_is_transition_type(const SeqRetimingKey *key)
static void seq_retiming_fix_transition(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
bool SEQ_retiming_selection_contains(const Editing *ed, const SeqRetimingKey *key)
int SEQ_retiming_key_index_get(const Sequence *seq, const SeqRetimingKey *key)
eRangeType
@ TRANSITION
@ LINEAR
void SEQ_retiming_key_timeline_frame_set(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const int timeline_frame)
SeqRetimingKey * SEQ_retiming_last_key_get(const Sequence *seq)
SeqRetimingKey * SEQ_retiming_add_freeze_frame(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const int offset)
static void seq_retiming_cleanup_freeze_frame(SeqRetimingKey *key)
SeqRetimingKey * SEQ_retiming_add_transition(const Scene *scene, Sequence *seq, SeqRetimingKey *key, float offset)
void SEQ_retiming_selection_remove(SeqRetimingKey *key)
static SeqRetimingKey * seq_retiming_add_key(const Scene *, Sequence *seq, float frame_index)
static void seq_retiming_segment_as_line_segment(const SeqRetimingKey *start_key, double r_v1[2], double r_v2[2])
bool SEQ_retiming_is_allowed(const Sequence *seq)
int SEQ_retiming_key_timeline_frame_get(const Scene *scene, const Sequence *seq, const SeqRetimingKey *key)
void SEQ_retiming_remove_key(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
SeqRetimingKey * SEQ_retiming_transition_start_get(SeqRetimingKey *key)
SeqRetimingKey * SEQ_retiming_find_segment_start_key(const Sequence *seq, float frame_index)
static int seq_retiming_clamp_timeline_frame(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const int timeline_frame)
static void seq_retiming_key_offset(const Scene *scene, Sequence *seq, SeqRetimingKey *key, const int offset)
void SEQ_retiming_selection_append(SeqRetimingKey *key)
static void retiming_key_overlap(Scene *scene, Sequence *seq)
static void seq_retiming_remove_key_ex(Sequence *seq, SeqRetimingKey *key)
SeqRetimingKey * SEQ_retiming_key_get_by_timeline_frame(const Scene *scene, const Sequence *seq, const int timeline_frame)
blender::Map< SeqRetimingKey *, Sequence * > SEQ_retiming_selection_get(const Editing *ed)
float SEQ_retiming_key_speed_get(const Sequence *seq, const SeqRetimingKey *key)
static void seq_retiming_line_segments_tangent_circle(const SeqRetimingKey *start_key, double r_center[2], double *radius)
float seq_retiming_evaluate(const Sequence *seq, const float frame_index)
void SEQ_retiming_transition_key_frame_set(const Scene *scene, const Sequence *seq, SeqRetimingKey *key, const int timeline_frame)
void SEQ_retiming_data_ensure(Sequence *seq)
bool SEQ_retiming_is_last_key(const Sequence *seq, const SeqRetimingKey *key)
void SEQ_retiming_data_clear(Sequence *seq)
bool SEQ_retiming_selection_clear(const Editing *ed)
void SEQ_retiming_sound_animation_data_set(const Scene *scene, const Sequence *seq)
static float seq_retiming_clamp_create_offset(SeqRetimingKey *key, float offset)
void SEQ_retiming_reset(Scene *scene, Sequence *seq)
bool SEQ_retiming_key_is_transition_start(const SeqRetimingKey *key)
void SEQ_retiming_selection_copy(SeqRetimingKey *dst, const SeqRetimingKey *src)
static float seq_retiming_evaluate_arc_segment(const SeqRetimingKey *key, const float frame_index)
static void seq_retiming_fix_transitions(const Scene *scene, Sequence *seq, SeqRetimingKey *key)
static bool seq_retiming_transition_is_linear(const Sequence *seq, const SeqRetimingKey *key)
static int seq_retiming_segment_length_get(const SeqRetimingKey *start_key)
SeqRetimingKey * SEQ_retiming_add_key(const Scene *scene, Sequence *seq, const int timeline_frame)
bool SEQ_retiming_selection_has_whole_transition(const Editing *ed, SeqRetimingKey *key)
void SEQ_time_update_meta_strip_range(const Scene *scene, Sequence *seq_meta)
int SEQ_time_get_rounded_sound_offset(const Scene *scene, const Sequence *seq)
int SEQ_time_left_handle_frame_get(const Scene *, const Sequence *seq)
void seq_time_update_effects_strip_range(const Scene *scene, const blender::Span< Sequence * > effects)
float SEQ_time_start_frame_get(const Sequence *seq)
float SEQ_time_media_playback_rate_factor_get(const Scene *scene, const Sequence *seq)
Definition strip_time.cc:38
int SEQ_time_right_handle_frame_get(const Scene *scene, const Sequence *seq)
void SEQ_transform_handle_overlap(Scene *scene, ListBase *seqbasep, blender::Span< Sequence * > transformed_strips, bool use_sync_markers)
ListBase * seqbasep
double original_strip_frame_index
struct SeqRetimingKey * retiming_keys
ccl_device_inline int abs(int x)
Definition util/math.h:120
ParamHandle ** handles