Blender V4.5
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
8
9#include <algorithm>
10
11#include "MEM_guardedalloc.h"
12
13#include "DNA_scene_types.h"
14#include "DNA_sequence_types.h"
15
16#include "BLI_bounds.hh"
17#include "BLI_listbase.h"
18#include "BLI_map.hh"
19#include "BLI_math_geom.h"
20#include "BLI_math_vector.h"
22#include "BLI_span.hh"
23#include "BLI_vector.hh"
24
25#include "BKE_sound.h"
26
27#include "SEQ_iterator.hh"
28#include "SEQ_retiming.hh"
29#include "SEQ_sequencer.hh"
30#include "SEQ_time.hh"
31#include "SEQ_transform.hh"
32
33#include "sequencer.hh"
34#include "strip_time.hh"
35
36namespace blender::seq {
37
43
44bool retiming_is_last_key(const Strip *strip, const SeqRetimingKey *key)
45{
46 return retiming_key_index_get(strip, key) == strip->retiming_keys_num - 1;
47}
48
50{
51 return strip->retiming_keys + strip->retiming_keys_num - 1;
52}
53
54int retiming_key_index_get(const Strip *strip, const SeqRetimingKey *key)
55{
56 return key - strip->retiming_keys;
57}
58
59static int content_frame_index_get(const Scene *scene,
60 const Strip *strip,
61 const int timeline_frame)
62{
63 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
64 const int sound_offset = time_get_rounded_sound_offset(strip, scene_fps);
65 return (timeline_frame - time_start_frame_get(strip) - sound_offset) *
67}
68
70 const Strip *strip,
71 const int timeline_frame)
72{
73 for (auto &key : retiming_keys_get(strip)) {
74 const int key_timeline_frame = retiming_key_timeline_frame_get(scene, strip, &key);
75 if (key_timeline_frame == timeline_frame) {
76 return &key;
77 }
78 }
79
80 return nullptr;
81}
82
83SeqRetimingKey *retiming_find_segment_start_key(const Strip *strip, float frame_index)
84{
85 SeqRetimingKey *start_key = nullptr;
86 for (auto &key : retiming_keys_get(strip)) {
87 if (retiming_is_last_key(strip, &key)) {
88 break;
89 }
90 if (key.strip_frame_index > frame_index) {
91 break;
92 }
93
94 start_key = &key;
95 }
96
97 return start_key;
98}
99
100int retiming_keys_count(const Strip *strip)
101{
102 return strip->retiming_keys_num;
103}
104
106{
107 if (!retiming_is_allowed(strip)) {
108 return;
109 }
110
111 if (retiming_is_active(strip)) {
112 return;
113 }
114
116 SeqRetimingKey *key = strip->retiming_keys + 1;
117 key->strip_frame_index = strip->len - 1;
118 key->retiming_factor = 1.0f;
119 strip->retiming_keys_num = 2;
120}
121
123{
124 if (strip->retiming_keys != nullptr) {
125 MEM_freeN(strip->retiming_keys);
126 strip->retiming_keys = nullptr;
127 strip->retiming_keys_num = 0;
128 }
129 strip->flag &= ~SEQ_SHOW_RETIMING;
130}
131
132static void retiming_key_overlap(Scene *scene, Strip *strip)
133{
134 ListBase *seqbase = active_seqbase_get(editing_get(scene));
137 dependant.add(strip);
138 iterator_set_expand(scene, seqbase, dependant, query_strip_effect_chain);
139 strips.add_multiple(dependant);
140 dependant.remove(strip);
141 transform_handle_overlap(scene, seqbase, strips, dependant, true);
142}
143
144void retiming_reset(Scene *scene, Strip *strip)
145{
146 if (!retiming_is_allowed(strip)) {
147 return;
148 }
149
150 retiming_data_clear(strip);
151
155
156 retiming_key_overlap(scene, strip);
157}
158
159bool retiming_is_active(const Strip *strip)
160{
161 return strip->retiming_keys_num > 1;
162}
163
165{
166 return strip->flag & SEQ_SHOW_RETIMING;
167}
168
169bool retiming_is_allowed(const Strip *strip)
170{
171 if (strip->len < 2) {
172 return false;
173 }
174
175 return ELEM(strip->type,
183}
184
186{
187 const SeqRetimingKey *end_key = start_key + 1;
188 return end_key->strip_frame_index - start_key->strip_frame_index;
189}
190
192{
193 const SeqRetimingKey *end_key = start_key + 1;
194 const double segment_length = strip_retiming_segment_length_get(start_key);
195 const double segment_fac_diff = end_key->retiming_factor - start_key->retiming_factor;
196 return segment_fac_diff / segment_length;
197}
198
200 double r_v1[2],
201 double r_v2[2])
202{
203 const SeqRetimingKey *end_key = start_key + 1;
204 r_v1[0] = start_key->strip_frame_index;
205 r_v1[1] = start_key->retiming_factor;
206 r_v2[0] = end_key->strip_frame_index;
207 r_v2[1] = end_key->retiming_factor;
208}
209
211 double r_center[2],
212 double *radius)
213{
214 blender::double2 s1_1, s1_2, s2_1, s2_2, p1_2;
215
216 /* Get 2 segments. */
217 strip_retiming_segment_as_line_segment(start_key - 1, s1_1, s1_2);
218 strip_retiming_segment_as_line_segment(start_key + 1, s2_1, s2_2);
219 /* Backup first segment end point - needed to calculate arc radius. */
220 copy_v2_v2_db(p1_2, s1_2);
221 /* Convert segments to vectors. */
223 sub_v2_v2v2_db(v1, s1_1, s1_2);
224 sub_v2_v2v2_db(v2, s2_1, s2_2);
225 /* Rotate segments by 90 degrees around seg. 1 end and seg. 2 start point. */
226 std::swap(v1[0], v1[1]);
227 std::swap(v2[0], v2[1]);
228 v1[0] *= -1;
229 v2[0] *= -1;
230 copy_v2_v2_db(s1_1, s1_2);
231 s1_2 += v1;
232 copy_v2_v2_db(s2_2, s2_1);
233 s2_2 += v2;
234 /* Get center and radius of arc segment between 2 linear segments. */
235 double lambda, mu;
236 isect_seg_seg_v2_lambda_mu_db(s1_1, s1_2, s2_1, s2_2, &lambda, &mu);
237 r_center[0] = s1_1[0] + lambda * (s1_2[0] - s1_1[0]);
238 r_center[1] = s1_1[1] + lambda * (s1_2[1] - s1_1[1]);
239 *radius = len_v2v2_db(p1_2, r_center);
240}
241
243{
244 return (key->flag & SEQ_SPEED_TRANSITION_IN) != 0 || (key->flag & SEQ_SPEED_TRANSITION_OUT) != 0;
245}
246
248{
249 return (key->flag & SEQ_SPEED_TRANSITION_IN) != 0;
250}
251
253{
254 if (key->flag & SEQ_SPEED_TRANSITION_OUT) {
255 return key - 1;
256 }
257 if (key->flag & SEQ_SPEED_TRANSITION_IN) {
258 return key;
259 }
260 return nullptr;
261}
262
264{
265 return (key->flag & SEQ_FREEZE_FRAME_IN) != 0 || (key->flag & SEQ_FREEZE_FRAME_OUT) != 0;
266}
267
268/* Check colinearity of 2 segments allowing for some imprecision.
269 * `isect_seg_seg_v2_lambda_mu_db()` return value does not work well in this case. */
270
271static bool strip_retiming_transition_is_linear(const Strip *strip, const SeqRetimingKey *key)
272{
273 const float prev_speed = retiming_key_speed_get(strip, key - 1);
274 const float next_speed = retiming_key_speed_get(strip, key + 2);
275
276 return abs(prev_speed - next_speed) < 0.01f;
277}
278
280 const float frame_index)
281{
282 double c[2], r;
284 const int side = c[1] > key->retiming_factor ? -1 : 1;
285 const float y = c[1] + side * sqrt(pow(r, 2) - pow((frame_index - c[0]), 2));
286 return y;
287}
288
289float strip_retiming_evaluate(const Strip *strip, const float frame_index)
290{
291 const SeqRetimingKey *start_key = retiming_find_segment_start_key(strip, frame_index);
292
293 const int start_key_index = start_key - strip->retiming_keys;
294 BLI_assert(start_key_index < strip->retiming_keys_num);
295
296 const float segment_frame_index = frame_index - start_key->strip_frame_index;
297
298 if (!retiming_key_is_transition_start(start_key)) {
299 const float segment_step = strip_retiming_segment_step_get(start_key);
300 return std::min(1.0f, start_key->retiming_factor + float(segment_step * segment_frame_index));
301 }
302
303 if (strip_retiming_transition_is_linear(strip, start_key)) {
304 const float segment_step = strip_retiming_segment_step_get(start_key - 1);
305 return std::min(1.0f, start_key->retiming_factor + float(segment_step * segment_frame_index));
306 }
307
308 /* Sanity check for transition type. */
309 BLI_assert(start_key_index > 0);
310 BLI_assert(start_key_index < strip->retiming_keys_num - 1);
311 UNUSED_VARS_NDEBUG(start_key_index);
312
313 return std::min(1.0f, strip_retiming_evaluate_arc_segment(start_key, frame_index));
314}
315
316static SeqRetimingKey *strip_retiming_add_key(Strip *strip, float frame_index)
317{
318 if (!retiming_is_allowed(strip)) {
319 return nullptr;
320 }
321 /* Clamp timeline frame to strip content range. */
322 if (frame_index <= 0) {
323 return &strip->retiming_keys[0];
324 }
325 if (frame_index >= retiming_last_key_get(strip)->strip_frame_index) {
326 return retiming_last_key_get(strip); /* This is expected for strips with no offsets. */
327 }
328
329 SeqRetimingKey *start_key = retiming_find_segment_start_key(strip, frame_index);
330
331 if (start_key->strip_frame_index == frame_index) {
332 return start_key; /* Retiming key already exists. */
333 }
334
335 if ((start_key->flag & SEQ_SPEED_TRANSITION_IN) != 0 ||
336 (start_key->flag & SEQ_FREEZE_FRAME_IN) != 0)
337 {
338 return nullptr;
339 }
340
341 float value = strip_retiming_evaluate(strip, frame_index);
342
343 SeqRetimingKey *keys = strip->retiming_keys;
344 const int keys_count = retiming_keys_count(strip);
345 const int new_key_index = start_key - keys + 1;
346 BLI_assert(new_key_index >= 0);
347 BLI_assert(new_key_index < keys_count);
348
349 SeqRetimingKey *new_keys = MEM_calloc_arrayN<SeqRetimingKey>(keys_count + 1, __func__);
350 if (new_key_index > 0) {
351 memcpy(new_keys, keys, new_key_index * sizeof(SeqRetimingKey));
352 }
353 if (new_key_index < keys_count) {
354 memcpy(new_keys + new_key_index + 1,
355 keys + new_key_index,
356 (keys_count - new_key_index) * sizeof(SeqRetimingKey));
357 }
358 MEM_freeN(keys);
359 strip->retiming_keys = new_keys;
360 strip->retiming_keys_num++;
361
362 SeqRetimingKey *added_key = (new_keys + new_key_index);
363 added_key->strip_frame_index = frame_index;
364 added_key->retiming_factor = value;
365
366 return added_key;
367}
368
369SeqRetimingKey *retiming_add_key(const Scene *scene, Strip *strip, const int timeline_frame)
370{
371 return strip_retiming_add_key(strip, content_frame_index_get(scene, strip, timeline_frame));
372}
373
375 const Strip *strip,
376 SeqRetimingKey *key,
377 const int timeline_frame)
378{
380 SeqRetimingKey *key_end = key_start + 1;
381 const float start_frame_index = key_start->strip_frame_index;
382 const float midpoint = key_start->original_strip_frame_index;
383 const float new_frame_index = content_frame_index_get(scene, strip, timeline_frame);
384 float new_midpoint_offset = new_frame_index - midpoint;
385 const float prev_segment_step = strip_retiming_segment_step_get(key_start - 1);
386 const float next_segment_step = strip_retiming_segment_step_get(key_end);
387
388 /* Prevent keys crossing eachother. */
389 SeqRetimingKey *prev_segment_end = key_start - 1, *next_segment_start = key_end + 1;
390 const float offset_max_left = midpoint - prev_segment_end->strip_frame_index - 1;
391 const float offset_max_right = next_segment_start->strip_frame_index - midpoint - 1;
392 new_midpoint_offset = fabs(new_midpoint_offset);
393 new_midpoint_offset = min_fff(new_midpoint_offset, offset_max_left, offset_max_right);
394 new_midpoint_offset = max_ff(new_midpoint_offset, 1);
395
396 key_start->strip_frame_index = midpoint - new_midpoint_offset;
397 key_end->strip_frame_index = midpoint + new_midpoint_offset;
398
399 const float offset = key_start->strip_frame_index - start_frame_index;
400 key_start->retiming_factor += offset * prev_segment_step;
401 key_end->retiming_factor -= offset * next_segment_step;
402}
403
405{
406 if ((key->flag & SEQ_FREEZE_FRAME_IN) != 0) {
407 SeqRetimingKey *next_key = key + 1;
409 next_key->flag &= ~SEQ_FREEZE_FRAME_OUT;
410 }
411 if ((key->flag & SEQ_FREEZE_FRAME_OUT) != 0) {
412 SeqRetimingKey *previous_key = key - 1;
414 previous_key->flag &= ~SEQ_FREEZE_FRAME_IN;
415 }
416}
417
419{
420 /* Transitions need special treatment, so separate these from `keys_to_remove`. */
422
423 /* Cleanup freeze frames and extract transition keys. */
424 for (SeqRetimingKey *key : keys_to_remove) {
427 }
428 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0) {
429 transitions.append_non_duplicates(key);
430 transitions.append_non_duplicates(key + 1);
431 }
432 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
433 transitions.append_non_duplicates(key);
434 transitions.append_non_duplicates(key - 1);
435 }
436 }
437
438 /* Sanitize keys to be removed. */
439 keys_to_remove.remove_if([&](const SeqRetimingKey *key) {
440 return key->strip_frame_index == 0 || retiming_is_last_key(strip, key) ||
442 });
443
444 const size_t keys_count = retiming_keys_count(strip);
445 size_t new_keys_count = keys_count - keys_to_remove.size() - transitions.size() / 2;
446 SeqRetimingKey *new_keys = MEM_calloc_arrayN<SeqRetimingKey>(new_keys_count, __func__);
447 int keys_copied = 0;
448
449 /* Copy keys to new array. */
450 for (SeqRetimingKey &key : retiming_keys_get(strip)) {
451 /* Create key that was used to make transition in new array. */
452 if (transitions.contains(&key) && retiming_key_is_transition_start(&key)) {
453 SeqRetimingKey *new_key = new_keys + keys_copied;
456 keys_copied++;
457 continue;
458 }
459 if (keys_to_remove.contains(&key) || transitions.contains(&key)) {
460 continue;
461 }
462 memcpy(new_keys + keys_copied, &key, sizeof(SeqRetimingKey));
463 keys_copied++;
464 }
465
466 MEM_freeN(strip->retiming_keys);
467 strip->retiming_keys = new_keys;
468 strip->retiming_keys_num = new_keys_count;
469}
470
472{
473 if (key->strip_frame_index == 0 || retiming_is_last_key(strip, key)) {
474 return; /* First and last key can not be removed. */
475 }
476
479 }
480
481 size_t keys_count = retiming_keys_count(strip);
482 SeqRetimingKey *keys = MEM_calloc_arrayN<SeqRetimingKey>(keys_count - 1, __func__);
483
484 const int key_index = key - strip->retiming_keys;
485 memcpy(keys, strip->retiming_keys, (key_index) * sizeof(SeqRetimingKey));
486 memcpy(keys + key_index,
487 strip->retiming_keys + key_index + 1,
488 (keys_count - key_index - 1) * sizeof(SeqRetimingKey));
489 MEM_freeN(strip->retiming_keys);
490 strip->retiming_keys = keys;
491 strip->retiming_keys_num--;
492}
493
494/* This function removes transition segment and creates retiming key where it originally was. */
496{
497 SeqRetimingKey *transition_start = key;
498 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
499 transition_start = key - 1;
500 }
501
502 const float orig_frame_index = transition_start->original_strip_frame_index;
503 const float orig_retiming_factor = transition_start->original_retiming_factor;
504
505 /* Remove both keys defining transition. */
506 int key_index = retiming_key_index_get(strip, transition_start);
507 strip_retiming_remove_key_ex(strip, transition_start);
508 strip_retiming_remove_key_ex(strip, strip->retiming_keys + key_index);
509
510 /* Create original linear key. */
511 SeqRetimingKey *orig_key = strip_retiming_add_key(strip, orig_frame_index);
512 orig_key->retiming_factor = orig_retiming_factor;
513 return orig_key;
514}
515
517{
518
521 return;
522 }
523
525}
526
528 const Strip *strip,
529 SeqRetimingKey *key)
530{
531 Bounds<float> max_tml_frame_offset = {MINAFRAMEF, MAXFRAMEF};
532
533 if (key->strip_frame_index != 0) {
534 SeqRetimingKey *prev_key = key - 1;
535 max_tml_frame_offset.min = retiming_key_timeline_frame_get(scene, strip, prev_key) -
536 retiming_key_timeline_frame_get(scene, strip, key);
537 }
538 if (!retiming_is_last_key(strip, key)) {
539 SeqRetimingKey *next_key = key + 1;
540 max_tml_frame_offset.max = retiming_key_timeline_frame_get(scene, strip, next_key) -
541 retiming_key_timeline_frame_get(scene, strip, key);
542 }
543 return max_tml_frame_offset;
544}
545
546/* Create pair of retiming keys separated by offset. */
547static std::pair<SeqRetimingKey *, SeqRetimingKey *> freeze_key_pair_create(const Scene *scene,
548 Strip *strip,
549 SeqRetimingKey *key,
550 const int offset)
551{
552
553 Bounds<float> max_offset = strip_retiming_clamp_bounds_get(scene, strip, key);
554 const float tml_frame_offset = math::clamp(float(offset), max_offset.min, max_offset.max);
555 const int orig_timeline_frame = retiming_key_timeline_frame_get(scene, strip, key);
556
557 /* Offset last key first, then add a freeze start key before it, because it is not possible to
558 * add keys after last one. */
559 if (retiming_is_last_key(strip, key)) {
560 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
561 const float frame_index_offset = tml_frame_offset *
562 time_media_playback_rate_factor_get(strip, scene_fps);
563 key->strip_frame_index += frame_index_offset;
564 SeqRetimingKey *freeze_start = retiming_add_key(scene, strip, orig_timeline_frame);
565
566 if (freeze_start == nullptr) {
567 key->strip_frame_index -= frame_index_offset;
568 return {nullptr, nullptr};
569 }
570 return {freeze_start, freeze_start + 1};
571 }
572
573 SeqRetimingKey *freeze_end = retiming_add_key(
574 scene, strip, orig_timeline_frame + tml_frame_offset);
575
576 if (freeze_end == nullptr) {
577 return {nullptr, nullptr};
578 }
579
580 return {freeze_end - 1, freeze_end};
581}
582
583/* This function tags previous key as freeze frame key. This is only a convenient way to prevent
584 * creating speed transitions. When freeze frame is deleted, this flag should be cleared. */
586 Strip *strip,
587 SeqRetimingKey *key,
588 const int offset)
589{
591 return nullptr;
592 }
593
594 const float orig_retiming_factor = key->retiming_factor;
595 std::pair<SeqRetimingKey *, SeqRetimingKey *> freeze_keys = freeze_key_pair_create(
596 scene, strip, key, offset);
597
598 if (freeze_keys.first == nullptr) {
599 return nullptr;
600 }
601
602 freeze_keys.first->flag |= SEQ_FREEZE_FRAME_IN;
603 freeze_keys.second->flag |= SEQ_FREEZE_FRAME_OUT;
604 freeze_keys.first->retiming_factor = orig_retiming_factor;
605 freeze_keys.second->retiming_factor = orig_retiming_factor;
606 return freeze_keys.second;
607}
608
610 Strip *strip,
611 SeqRetimingKey *key,
612 float offset)
613{
614 BLI_assert(!retiming_is_last_key(strip, key));
615 BLI_assert(key->strip_frame_index != 0);
616
617 SeqRetimingKey *prev_key = key - 1;
618 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0 ||
619 (prev_key->flag & SEQ_SPEED_TRANSITION_IN) != 0)
620 {
621 return nullptr;
622 }
623
624 if ((key->flag & SEQ_FREEZE_FRAME_IN) != 0 || (prev_key->flag & SEQ_FREEZE_FRAME_IN) != 0) {
625 return nullptr;
626 }
627
628 Bounds<float> max_offset = strip_retiming_clamp_bounds_get(scene, strip, key);
629 float clamped_offset = math::clamp(offset, max_offset.min, max_offset.max);
630
631 const int orig_key_index = retiming_key_index_get(strip, key);
632 const float orig_frame_index = key->strip_frame_index;
633 const float orig_retiming_factor = key->retiming_factor;
634
635 SeqRetimingKey *transition_out = strip_retiming_add_key(strip,
636 orig_frame_index + clamped_offset);
637 transition_out->flag |= SEQ_SPEED_TRANSITION_OUT;
638
639 SeqRetimingKey *transition_in = strip_retiming_add_key(strip, orig_frame_index - clamped_offset);
640 transition_in->flag |= SEQ_SPEED_TRANSITION_IN;
641 transition_in->original_strip_frame_index = orig_frame_index;
642 transition_in->original_retiming_factor = orig_retiming_factor;
643
644 strip_retiming_remove_key_ex(strip, strip->retiming_keys + orig_key_index + 1);
645 return strip->retiming_keys + orig_key_index + 1;
646}
647
649 const Strip *strip,
650 SeqRetimingKey *start_key,
651 float offset)
652{
653 SeqRetimingKey *end_key = start_key + 1;
654 SeqRetimingKey *prev_key = start_key - 1;
655 SeqRetimingKey *next_key = start_key + 2;
656 const float prev_max_offset = prev_key->strip_frame_index - start_key->strip_frame_index;
657 const float next_max_offset = next_key->strip_frame_index - end_key->strip_frame_index;
658 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
659 const float min_step = time_media_playback_rate_factor_get(strip, scene_fps);
660
661 return std::clamp(offset, prev_max_offset + min_step, next_max_offset - min_step);
662}
663
665 Strip *strip,
666 SeqRetimingKey *key,
667 const float offset)
668{
669 float clamped_offset = strip_retiming_clamp_transition_offset(scene, strip, key, offset);
670 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
671 const float duration = (key->original_strip_frame_index - key->strip_frame_index) /
672 time_media_playback_rate_factor_get(strip, scene_fps);
673 const bool was_selected = retiming_selection_contains(editing_get(scene), key);
674
675 SeqRetimingKey *original_key = strip_retiming_remove_transition(strip, key);
676 original_key->strip_frame_index += clamped_offset;
677
678 SeqRetimingKey *transition_out = retiming_add_transition(scene, strip, original_key, duration);
679
680 if (was_selected) {
681 retiming_selection_append(transition_out);
682 retiming_selection_append(transition_out - 1);
683 }
684}
685
687 Strip *strip,
688 SeqRetimingKey *key,
689 const int timeline_frame)
690{
691 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0) {
692 return timeline_frame;
693 }
694
695 int prev_key_timeline_frame = -MAXFRAME;
696 int next_key_timeline_frame = MAXFRAME;
697
698 if (key->strip_frame_index > 0) {
699 SeqRetimingKey *prev_key = key - 1;
700 prev_key_timeline_frame = retiming_key_timeline_frame_get(scene, strip, prev_key);
701 }
702
703 if (!retiming_is_last_key(strip, key)) {
704 SeqRetimingKey *next_key = key + 1;
705 next_key_timeline_frame = retiming_key_timeline_frame_get(scene, strip, next_key);
706 }
707
708 return std::clamp(timeline_frame, prev_key_timeline_frame + 1, next_key_timeline_frame - 1);
709}
710
711/* Remove and re-create transition. This way transition won't change length.
712 * Alternative solution is to find where in arc segment the `y` value is closest to key
713 * retiming factor, then trim transition to that point. This would change transition length. */
714
715static void strip_retiming_fix_transition(const Scene *scene, Strip *strip, SeqRetimingKey *key)
716{
717 const int keys_num = strip->retiming_keys_num;
718 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
719 const float transition_duration = (key->original_strip_frame_index - key->strip_frame_index) /
720 time_media_playback_rate_factor_get(strip, scene_fps);
721 SeqRetimingKey *orig_key = strip_retiming_remove_transition(strip, key);
722 retiming_add_transition(scene, strip, orig_key, transition_duration);
723 BLI_assert(keys_num == strip->retiming_keys_num);
724 UNUSED_VARS_NDEBUG(keys_num);
725}
726
727static void strip_retiming_fix_transitions(const Scene *scene, Strip *strip, SeqRetimingKey *key)
728{
729 /* Store value, since handles array will be reallocated. */
730 const int key_index = retiming_key_index_get(strip, key);
731
732 if (key_index > 1) {
733 SeqRetimingKey *prev_key = key - 2;
734 if (retiming_key_is_transition_start(prev_key)) {
735 strip_retiming_fix_transition(scene, strip, prev_key);
736 }
737 }
738
739 if (!retiming_is_last_key(strip, key)) {
740 SeqRetimingKey *next_key = &retiming_keys_get(strip)[key_index + 1];
741 if (retiming_key_is_transition_start(next_key)) {
742 strip_retiming_fix_transition(scene, strip, next_key);
743 }
744 }
745}
746
747static void strip_retiming_key_offset(const Scene *scene,
748 Strip *strip,
749 SeqRetimingKey *key,
750 const float offset)
751{
752 if ((key->flag & SEQ_SPEED_TRANSITION_IN) != 0) {
753 strip_retiming_transition_offset(scene, strip, key, offset);
754 }
755 else {
756 key->strip_frame_index += offset;
757 strip_retiming_fix_transitions(scene, strip, key);
758 }
759}
760
762 const Strip *strip,
763 const SeqRetimingKey *key)
764{
765 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
766 const int sound_offset = time_get_rounded_sound_offset(strip, scene_fps);
767 return round_fl_to_int(time_start_frame_get(strip) + sound_offset +
768 key->strip_frame_index /
769 time_media_playback_rate_factor_get(strip, scene_fps));
770}
771
773 Strip *strip,
774 SeqRetimingKey *key,
775 const int timeline_frame)
776{
777 if ((key->flag & SEQ_SPEED_TRANSITION_OUT) != 0) {
778 return;
779 }
780
781 const int orig_timeline_frame = retiming_key_timeline_frame_get(scene, strip, key);
782 const int clamped_timeline_frame = strip_retiming_clamp_timeline_frame(
783 scene, strip, key, timeline_frame);
784 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
785 const float offset = (clamped_timeline_frame - orig_timeline_frame) *
786 time_media_playback_rate_factor_get(strip, scene_fps);
787
788 const int key_count = retiming_keys_get(strip).size();
789 const int key_index = retiming_key_index_get(strip, key);
790
791 if (orig_timeline_frame == time_right_handle_frame_get(scene, strip)) {
792 for (int i = key_index; i < key_count; i++) {
793 SeqRetimingKey *key_iter = &retiming_keys_get(strip)[i];
794 strip_retiming_key_offset(scene, strip, key_iter, offset);
795 }
796 }
797 else if (orig_timeline_frame == time_left_handle_frame_get(scene, strip) ||
798 key->strip_frame_index == 0)
799 {
800 strip->start += clamped_timeline_frame - orig_timeline_frame;
801 for (int i = key_index + 1; i < key_count; i++) {
802 SeqRetimingKey *key_iter = &retiming_keys_get(strip)[i];
803 strip_retiming_key_offset(scene, strip, key_iter, -offset);
804 }
805 }
806 else {
807 strip_retiming_key_offset(scene, strip, key, offset);
808 }
809
813}
814
815float retiming_key_speed_get(const Strip *strip, const SeqRetimingKey *key)
816{
817 if (key->strip_frame_index == 0) {
818 return 1.0f;
819 }
820
821 const SeqRetimingKey *key_prev = key - 1;
822 const int frame_index_max = strip->len - 1;
823 const float frame_index_start = round_fl_to_int(key_prev->retiming_factor * frame_index_max);
824 const float frame_index_end = round_fl_to_int(key->retiming_factor * frame_index_max);
825 const float segment_content_frame_count = frame_index_end - frame_index_start;
826 const float segment_length = key->strip_frame_index - key_prev->strip_frame_index;
827 const float speed = segment_content_frame_count / segment_length;
828 return speed;
829}
830
832 const Scene *scene, Strip *strip, SeqRetimingKey *key, const float speed, bool keep_retiming)
833{
834 if (key->strip_frame_index == 0) {
835 return;
836 }
837
838 const SeqRetimingKey *key_prev = key - 1;
839
840 const int frame_index_max = strip->len - 1;
841 const float frame_index_prev = round_fl_to_int(key_prev->retiming_factor * frame_index_max);
842 const float frame_index = round_fl_to_int(key->retiming_factor * frame_index_max);
843
844 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
845 const float segment_timeline_duration = (frame_index - frame_index_prev) /
846 time_media_playback_rate_factor_get(strip, scene_fps);
847 const float new_timeline_duration = segment_timeline_duration / speed;
848
849 const float orig_timeline_frame = retiming_key_timeline_frame_get(scene, strip, key);
850 const float new_timeline_frame = std::round(
851 retiming_key_timeline_frame_get(scene, strip, key_prev) + new_timeline_duration);
852
853 retiming_key_timeline_frame_set(scene, strip, key, new_timeline_frame);
854
855 if (keep_retiming) {
856 const int key_index = retiming_key_index_get(strip, key);
857 const int offset = new_timeline_frame - orig_timeline_frame;
858 for (int i = key_index + 1; i < retiming_keys_count(strip); i++) {
859 SeqRetimingKey *key_iter = &retiming_keys_get(strip)[i];
860 strip_retiming_key_offset(scene, strip, key_iter, offset);
861 }
862 }
863}
864
868};
869
877
879 public:
880 int start, end;
881 float speed;
883
885 RetimingRange(const Strip *strip, int start_frame, int end_frame, float speed, eRangeType type)
886 : start(start_frame), end(end_frame), speed(speed), type(type)
887 {
888 if (type == TRANSITION) {
889 this->speed = 1.0f;
891 }
892 }
893
894 RetimingRange(int start_frame, int end_frame, float speed, eRangeType type)
895 : start(start_frame), end(end_frame), speed(speed), type(type)
896 {
897 }
898
900 {
902 for (int i = 0; i < speed_table.size(); i++) {
903 new_range.speed_table.append(speed_table[i]);
904 }
905 return new_range;
906 }
907
908 /* Create new range representing overlap of 2 ranges.
909 * Returns overlapping range. */
911 {
912 RetimingRange new_range = RetimingRange(0, 0, 0, LINEAR);
913
914 /* Offsets to merge speed tables. */
915 int range_offset = 0, rhs_range_offset = 0;
916 if (intersect_type(rhs_range) == FULL) {
917 new_range.start = start;
918 new_range.end = end;
919 rhs_range_offset = start - rhs_range.start;
920 }
921 else if (intersect_type(rhs_range) == PARTIAL_START) {
922 new_range.start = start;
923 new_range.end = rhs_range.end;
924 rhs_range_offset = start - rhs_range.start;
925 }
926 else if (intersect_type(rhs_range) == PARTIAL_END) {
927 new_range.start = rhs_range.start;
928 new_range.end = end;
929 range_offset = rhs_range.start - start;
930 }
931 else if (intersect_type(rhs_range) == INSIDE) {
932 new_range.start = rhs_range.start;
933 new_range.end = rhs_range.end;
934 range_offset = rhs_range.start - start;
935 }
936
937 if (type != TRANSITION && rhs_range.type != TRANSITION) {
938 new_range.speed = speed * rhs_range.speed;
939 return new_range;
940 }
941
942 /* One of ranges is transition type, so speed tables has to be copied. */
943 new_range.type = TRANSITION;
944 new_range.speed = 1.0f;
945 const int new_range_len = new_range.end - new_range.start;
946
947 if (type == TRANSITION && rhs_range.type == TRANSITION) {
948 for (int i = 0; i < new_range_len; i++) {
949 const float range_speed = speed_table[i + range_offset];
950 const float rhs_range_speed = rhs_range.speed_table[i + rhs_range_offset];
951 new_range.speed_table.append(range_speed * rhs_range_speed);
952 }
953 }
954 else if (type == TRANSITION) {
955 for (int i = 0; i < new_range_len; i++) {
956 const float range_speed = speed_table[i + range_offset];
957 new_range.speed_table.append(range_speed * rhs_range.speed);
958 }
959 }
960 else if (rhs_range.type == TRANSITION) {
961 for (int i = 0; i < new_range_len; i++) {
962 const float rhs_range_speed = rhs_range.speed_table[i + rhs_range_offset];
963 new_range.speed_table.append(speed * rhs_range_speed);
964 }
965 }
966
967 return new_range;
968 }
969
971 {
972 for (int timeline_frame = start; timeline_frame <= end; timeline_frame++) {
973 /* We need number actual number of frames here. */
974 const double normal_step = 1 / double(strip->len - 1);
975
976 const int frame_index = timeline_frame - time_start_frame_get(strip);
977 /* Who needs calculus, when you can have slow code? */
978 const double val_prev = strip_retiming_evaluate(strip, frame_index - 1);
979 const double val = strip_retiming_evaluate(strip, frame_index);
980 const double speed_at_frame = (val - val_prev) / normal_step;
981 speed_table.append(speed_at_frame);
982 }
983 }
984
986 {
987 if (other.start <= start && other.end >= end) {
988 return FULL;
989 }
990 if (other.start > start && other.start < end && other.end > start && other.end < end) {
991 return INSIDE;
992 }
993 if (other.start > start && other.start < end) {
994 return PARTIAL_END;
995 }
996 if (other.end > start && other.end < end) {
997 return PARTIAL_START;
998 }
999 return NONE;
1000 }
1001};
1002
1004 public:
1007 {
1008 for (const SeqRetimingKey &key : retiming_keys_get(strip)) {
1009 if (key.strip_frame_index == 0) {
1010 continue;
1011 }
1012 const SeqRetimingKey *key_prev = &key - 1;
1013 float speed = retiming_key_speed_get(strip, &key);
1014 int frame_start = time_start_frame_get(strip) + key_prev->strip_frame_index;
1015 int frame_end = time_start_frame_get(strip) + key.strip_frame_index;
1016
1018 RetimingRange range = RetimingRange(strip, frame_start, frame_end, speed, type);
1019 ranges.append(range);
1020 }
1021 }
1022
1024 {
1025 if (ranges.is_empty()) {
1026 for (const RetimingRange &rhs_range : rhs.ranges) {
1028 rhs_range.start, rhs_range.end, rhs_range.speed, rhs_range.type);
1029 ranges.append(range);
1030 }
1031 return *this;
1032 }
1033
1034 for (int i = 0; i < ranges.size(); i++) {
1035 RetimingRange &range = ranges[i];
1036 for (const RetimingRange &rhs_range : rhs.ranges) {
1037 if (range.intersect_type(rhs_range) == NONE) {
1038 continue;
1039 }
1040 if (range.intersect_type(rhs_range) == FULL) {
1041 RetimingRange isect = range * rhs_range;
1042 ranges.remove(i);
1043 ranges.insert(i, isect);
1044 }
1045 if (range.intersect_type(rhs_range) == PARTIAL_START) {
1046 ranges.insert(i, range * rhs_range);
1047 ranges.insert(i, range * rhs_range);
1048 range.start = rhs_range.end + 1;
1049 }
1050 else if (range.intersect_type(rhs_range) == PARTIAL_END) {
1051 ranges.insert(i, range * rhs_range);
1052 range.end = rhs_range.start;
1053 }
1054 else if (range.intersect_type(rhs_range) == INSIDE) {
1055 RetimingRange left_range = range.duplicate();
1056 left_range.end = rhs_range.start;
1057 range.start = rhs_range.end + 1;
1058
1059 ranges.insert(i, left_range);
1060 ranges.insert(i, range * rhs_range);
1061 }
1062 }
1063 }
1064 return *this;
1065 }
1066};
1067
1069{
1070 RetimingRangeData strip_retiming_data = RetimingRangeData(strip);
1071
1072 const Strip *meta_parent = lookup_meta_by_strip(scene->ed, strip);
1073 if (meta_parent == nullptr) {
1074 return strip_retiming_data;
1075 }
1076
1077 RetimingRangeData meta_retiming_data = RetimingRangeData(meta_parent);
1078 strip_retiming_data *= meta_retiming_data;
1079 return strip_retiming_data;
1080}
1081
1082void retiming_sound_animation_data_set(const Scene *scene, const Strip *strip)
1083{
1084 /* Content cut off by `anim_startofs` is as if it does not exist for sequencer. But Audaspace
1085 * seeking relies on having animation buffer initialized for whole sequence. */
1086 if (strip->anim_startofs > 0) {
1087 const int strip_start = time_start_frame_get(strip);
1089 strip->scene_sound, strip_start - strip->anim_startofs, strip_start, 1.0f);
1090 }
1091
1092 const float scene_fps = float(scene->r.frs_sec) / float(scene->r.frs_sec_base);
1093 const int sound_offset = time_get_rounded_sound_offset(strip, scene_fps);
1094
1095 RetimingRangeData retiming_data = strip_retiming_range_data_get(scene, strip);
1096 for (int i = 0; i < retiming_data.ranges.size(); i++) {
1097 RetimingRange range = retiming_data.ranges[i];
1098 if (range.type == TRANSITION) {
1099
1100 const int range_length = range.end - range.start;
1101 for (int i = 0; i <= range_length; i++) {
1102 const int frame = range.start + i;
1104 strip->scene_sound, frame + sound_offset, range.speed_table[i], true);
1105 }
1106 }
1107 else {
1109 strip->scene_sound, range.start + sound_offset, range.end + sound_offset, range.speed);
1110 }
1111 }
1112}
1113
1115{
1116 bool was_empty = true;
1117
1118 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1119 for (SeqRetimingKey &key : retiming_keys_get(strip)) {
1120 was_empty &= (key.flag & SEQ_KEY_SELECTED) == 0;
1121 key.flag &= ~SEQ_KEY_SELECTED;
1122 }
1123 }
1124 return !was_empty;
1125}
1126
1128
1129 SeqRetimingKey *key)
1130{
1131 key->flag |= SEQ_KEY_SELECTED;
1132}
1133
1138
1140{
1142 dst->flag |= (src->flag & SEQ_KEY_SELECTED);
1143}
1144
1146{
1148 if (!ed) {
1149 return selection;
1150 }
1151 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1152 for (SeqRetimingKey &key : retiming_keys_get(strip)) {
1153 if ((key.flag & SEQ_KEY_SELECTED) != 0) {
1154 selection.add(&key, strip);
1155 }
1156 }
1157 }
1158 return selection;
1159}
1160
1162{
1163 LISTBASE_FOREACH (Strip *, strip, ed->seqbasep) {
1164 for (const SeqRetimingKey &key_iter : retiming_keys_get(strip)) {
1165 if ((key_iter.flag & SEQ_KEY_SELECTED) != 0 && &key_iter == key) {
1166 return true;
1167 }
1168 }
1169 }
1170 return false;
1171}
1172
1174{
1176 SeqRetimingKey *key_end = key_start + 1;
1177 bool has_start = false, has_end = false;
1178
1180
1181 for (auto item : selection.items()) {
1182 if (item.key == key_start) {
1183 has_start = true;
1184 }
1185 if (item.key == key_end) {
1186 has_end = true;
1187 }
1188 if (has_start && has_end) {
1189 return true;
1190 }
1191 }
1192 return false;
1193}
1194
1195} // namespace blender::seq
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:46
#define LISTBASE_FOREACH(type, var, list)
MINLINE int round_fl_to_int(float a)
MINLINE float max_ff(float a, float b)
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 sub_v2_v2v2_db(double r[2], const double a[2], const double b[2])
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define MAXFRAMEF
#define MINAFRAMEF
#define MAXFRAME
@ SEQ_KEY_SELECTED
@ SEQ_SPEED_TRANSITION_OUT
@ SEQ_FREEZE_FRAME_OUT
@ SEQ_SPEED_TRANSITION_IN
@ SEQ_FREEZE_FRAME_IN
@ SEQ_SHOW_RETIMING
@ STRIP_TYPE_SCENE
@ STRIP_TYPE_MOVIECLIP
@ STRIP_TYPE_SOUND_RAM
@ STRIP_TYPE_IMAGE
@ STRIP_TYPE_MOVIE
@ STRIP_TYPE_META
@ STRIP_TYPE_MASK
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void append(const T &value)
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
ItemIterator items() const &
Definition BLI_map.hh:902
bool remove(const Key &key)
bool add(const Key &key)
void add_multiple(Span< Key > keys)
int64_t size() const
int64_t remove_if(Predicate &&predicate)
bool contains(const T &value) const
void append_non_duplicates(const T &value)
RetimingRangeData & operator*=(const RetimingRangeData &rhs)
RetimingRangeData(const Strip *strip)
blender::Vector< RetimingRange > ranges
RetimingRange(int start_frame, int end_frame, float speed, eRangeType type)
eIntersectType intersect_type(const RetimingRange &other) const
RetimingRange(const Strip *strip, int start_frame, int end_frame, float speed, eRangeType type)
RetimingRange operator*(const RetimingRange &rhs_range)
blender::Vector< float > speed_table
void claculate_speed_table_from_seq(const Strip *strip)
#define pow
#define abs
#define sqrt
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 fabs(const float2 a)
T clamp(const T &a, const T &min, const T &max)
int retiming_key_index_get(const Strip *strip, const SeqRetimingKey *key)
Strip * lookup_meta_by_strip(Editing *ed, const Strip *key)
int time_right_handle_frame_get(const Scene *scene, const Strip *strip)
static bool strip_retiming_transition_is_linear(const Strip *strip, const SeqRetimingKey *key)
SeqRetimingKey * retiming_add_transition(const Scene *scene, Strip *strip, SeqRetimingKey *key, float offset)
void retiming_key_speed_set(const Scene *scene, Strip *strip, SeqRetimingKey *key, const float speed, bool keep_retiming)
static SeqRetimingKey * strip_retiming_add_key(Strip *strip, float frame_index)
bool retiming_key_is_transition_start(const SeqRetimingKey *key)
bool retiming_selection_has_whole_transition(const Editing *ed, SeqRetimingKey *key)
static double strip_retiming_segment_length_get(const SeqRetimingKey *start_key)
void retiming_key_timeline_frame_set(const Scene *scene, Strip *strip, SeqRetimingKey *key, const int timeline_frame)
void retiming_reset(Scene *scene, Strip *strip)
bool retiming_selection_contains(const Editing *ed, const SeqRetimingKey *key)
int retiming_keys_count(const Strip *strip)
blender::Map< SeqRetimingKey *, Strip * > retiming_selection_get(const Editing *ed)
float time_media_playback_rate_factor_get(const Strip *strip, const float scene_fps)
Definition strip_time.cc:41
SeqRetimingKey * retiming_add_freeze_frame(const Scene *scene, Strip *strip, SeqRetimingKey *key, const int offset)
int retiming_key_timeline_frame_get(const Scene *scene, const Strip *strip, const SeqRetimingKey *key)
static void strip_retiming_key_offset(const Scene *scene, Strip *strip, SeqRetimingKey *key, const float offset)
static void strip_retiming_line_segments_tangent_circle(const SeqRetimingKey *start_key, double r_center[2], double *radius)
Editing * editing_get(const Scene *scene)
Definition sequencer.cc:272
blender::Span< Strip * > SEQ_lookup_effects_by_strip(Editing *ed, const Strip *key)
void retiming_remove_multiple_keys(Strip *strip, blender::Vector< SeqRetimingKey * > &keys_to_remove)
MutableSpan< SeqRetimingKey > retiming_keys_get(const Strip *strip)
SeqRetimingKey * retiming_transition_start_get(SeqRetimingKey *key)
void retiming_data_ensure(Strip *strip)
void retiming_remove_key(Strip *strip, SeqRetimingKey *key)
int time_left_handle_frame_get(const Scene *, const Strip *strip)
static SeqRetimingKey * strip_retiming_remove_transition(Strip *strip, SeqRetimingKey *key)
SeqRetimingKey * retiming_find_segment_start_key(const Strip *strip, float frame_index)
int time_get_rounded_sound_offset(const Strip *strip, const float frames_per_second)
void retiming_selection_remove(SeqRetimingKey *key)
void retiming_data_clear(Strip *strip)
float time_start_frame_get(const Strip *strip)
void query_strip_effect_chain(const Scene *scene, Strip *reference_strip, ListBase *seqbase, VectorSet< Strip * > &r_strips)
Definition iterator.cc:231
void strip_time_update_effects_strip_range(const Scene *scene, const blender::Span< Strip * > effects)
SeqRetimingKey * retiming_key_get_by_timeline_frame(const Scene *scene, const Strip *strip, const int timeline_frame)
bool retiming_is_active(const Strip *strip)
bool retiming_key_is_transition_type(const SeqRetimingKey *key)
void time_update_meta_strip_range(const Scene *scene, Strip *strip_meta)
bool retiming_key_is_freeze_frame(const SeqRetimingKey *key)
static void strip_retiming_remove_key_ex(Strip *strip, SeqRetimingKey *key)
bool retiming_data_is_editable(const Strip *strip)
void iterator_set_expand(const Scene *scene, ListBase *seqbase, VectorSet< Strip * > &strips, void strip_query_func(const Scene *scene, Strip *strip_reference, ListBase *seqbase, VectorSet< Strip * > &strips))
Definition iterator.cc:82
static Bounds< float > strip_retiming_clamp_bounds_get(const Scene *scene, const Strip *strip, SeqRetimingKey *key)
void retiming_selection_copy(SeqRetimingKey *dst, const SeqRetimingKey *src)
static void strip_retiming_transition_offset(const Scene *scene, Strip *strip, SeqRetimingKey *key, const float offset)
static void retiming_key_overlap(Scene *scene, Strip *strip)
void retiming_sound_animation_data_set(const Scene *scene, const Strip *strip)
bool retiming_selection_clear(const Editing *ed)
float retiming_key_speed_get(const Strip *strip, const SeqRetimingKey *key)
static float strip_retiming_evaluate_arc_segment(const SeqRetimingKey *key, const float frame_index)
static float strip_retiming_clamp_transition_offset(const Scene *scene, const Strip *strip, SeqRetimingKey *start_key, float offset)
bool retiming_is_allowed(const Strip *strip)
static void strip_retiming_segment_as_line_segment(const SeqRetimingKey *start_key, double r_v1[2], double r_v2[2])
void retiming_transition_key_frame_set(const Scene *scene, const Strip *strip, SeqRetimingKey *key, const int timeline_frame)
static float strip_retiming_segment_step_get(const SeqRetimingKey *start_key)
void transform_handle_overlap(Scene *scene, ListBase *seqbasep, blender::Span< Strip * > transformed_strips, bool use_sync_markers)
ListBase * active_seqbase_get(const Editing *ed)
Definition sequencer.cc:420
static int strip_retiming_clamp_timeline_frame(const Scene *scene, Strip *strip, SeqRetimingKey *key, const int timeline_frame)
static void strip_retiming_fix_transition(const Scene *scene, Strip *strip, SeqRetimingKey *key)
SeqRetimingKey * retiming_last_key_get(const Strip *strip)
void retiming_selection_append(SeqRetimingKey *key)
SeqRetimingKey * retiming_add_key(const Scene *scene, Strip *strip, const int timeline_frame)
bool retiming_is_last_key(const Strip *strip, const SeqRetimingKey *key)
static void strip_retiming_fix_transitions(const Scene *scene, Strip *strip, SeqRetimingKey *key)
float strip_retiming_evaluate(const Strip *strip, const float frame_index)
static RetimingRangeData strip_retiming_range_data_get(const Scene *scene, const Strip *strip)
static std::pair< SeqRetimingKey *, SeqRetimingKey * > freeze_key_pair_create(const Scene *scene, Strip *strip, SeqRetimingKey *key, const int offset)
static void strip_retiming_cleanup_freeze_frame(SeqRetimingKey *key)
static int content_frame_index_get(const Scene *scene, const Strip *strip, const int timeline_frame)
VecBase< double, 2 > double2
struct Editing * ed
struct RenderData r
double original_strip_frame_index
void * scene_sound
int retiming_keys_num
struct SeqRetimingKey * retiming_keys
i
Definition text_draw.cc:230
ParamHandle ** handles