Reported by Fedora team for gcc-13 and Andrew created patch here
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / edits.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2017 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "aedit.h"
23 #include "asset.h"
24 #include "assets.h"
25 #include "automation.h"
26 #include "bcsignals.h"
27 #include "cache.h"
28 #include "clip.h"
29 #include "clipedls.h"
30 #include "edit.h"
31 #include "edits.h"
32 #include "edl.h"
33 #include "edlsession.h"
34 #include "ffmpeg.h"
35 #include "file.h"
36 #include "filexml.h"
37 #include "filesystem.h"
38 #include "localsession.h"
39 #include "plugin.h"
40 #include "strategies.inc"
41 #include "track.h"
42 #include "transition.h"
43 #include "transportque.inc"
44
45 #include <string.h>
46
47 Edits::Edits(EDL *edl, Track *track)
48  : List<Edit>()
49 {
50         this->edl = edl;
51         this->track = track;
52 }
53
54 Edits::~Edits()
55 {
56 }
57
58
59 void Edits::equivalent_output(Edits *edits, int64_t *result)
60 {
61 // For the case of plugin sets, a new plugin set may be created with
62 // plugins only starting after 0.  We only want to restart brender at
63 // the first plugin in this case.
64         for(Edit *current = first, *that_current = edits->first;
65                 current || that_current;
66                 current = NEXT,
67                 that_current = that_current->next)
68         {
69 //printf("Edits::equivalent_output 1 %d\n", *result);
70                 if(!current && that_current)
71                 {
72                         int64_t position1 = length();
73                         int64_t position2 = that_current->startproject;
74                         if(*result < 0 || *result > MIN(position1, position2))
75                                 *result = MIN(position1, position2);
76                         break;
77                 }
78                 else
79                 if(current && !that_current)
80                 {
81                         int64_t position1 = edits->length();
82                         int64_t position2 = current->startproject;
83                         if(*result < 0 || *result > MIN(position1, position2))
84                                 *result = MIN(position1, position2);
85                         break;
86                 }
87                 else
88                 {
89 //printf("Edits::equivalent_output 2 %d\n", *result);
90                         current->equivalent_output(that_current, result);
91 //printf("Edits::equivalent_output 3 %d\n", *result);
92                 }
93         }
94 }
95
96 void Edits::copy_from(Edits *edits)
97 {
98         while(last) delete last;
99         for(Edit *current = edits->first; current; current = NEXT)
100         {
101                 Edit *new_edit = append(create_edit());
102                 new_edit->copy_from(current);
103         }
104 }
105
106
107 Edits& Edits::operator=(Edits& edits)
108 {
109 printf("Edits::operator= 1\n");
110         copy_from(&edits);
111         return *this;
112 }
113
114
115 void Edits::insert_asset(Asset *asset, EDL *nested_edl,
116         int64_t length, int64_t position, int track_number)
117 {
118         Edit *new_edit = insert_new_edit(position);
119
120         new_edit->nested_edl = nested_edl;
121         new_edit->asset = asset;
122         new_edit->startsource = 0;
123         new_edit->startproject = position;
124         new_edit->length = length;
125
126         if(nested_edl)
127         {
128                 if(track->data_type == TRACK_AUDIO)
129                         new_edit->channel = track_number % nested_edl->session->audio_channels;
130                 else
131                         new_edit->channel = 0;
132         }
133
134         if(asset && !nested_edl)
135         {
136                 if(asset->audio_data)
137                         new_edit->channel = track_number % asset->channels;
138                 else
139                 if(asset->video_data)
140                         new_edit->channel = track_number % asset->layers;
141         }
142
143 //printf("Edits::insert_asset %d %d\n", new_edit->channel, new_edit->length);
144         for(Edit *current = new_edit->next; current; current = NEXT)
145         {
146                 current->startproject += length;
147         }
148 }
149
150 void Edits::insert_edits(Edits *source_edits,
151         int64_t position,
152         int64_t min_length,
153         int edit_autos)
154 {
155         //int64_t clipboard_end = position + min_length;
156 // Length pasted so far
157         int64_t source_len = 0;
158
159 // Fill region between end of edit table and beginning of pasted segment
160 // with silence.  Can't call from insert_new_edit because it's recursive.
161         if(position > length())
162         {
163                 paste_silence(length(), position);
164         }
165
166
167         for(Edit *source_edit = source_edits->first;
168                 source_edit;
169                 source_edit = source_edit->next)
170         {
171                 EDL *dest_nested_edl = 0;
172                 if(source_edit->nested_edl)
173                         dest_nested_edl = edl->nested_edls.get_nested(source_edit->nested_edl);
174
175 // Update Assets
176                 Asset *dest_asset = 0;
177                 if(source_edit->asset)
178                         dest_asset = edl->assets->update(source_edit->asset);
179 // Open destination area
180                 Edit *dest_edit = insert_new_edit(position + source_edit->startproject);
181
182                 dest_edit->clone_from(source_edit);
183                 dest_edit->asset = dest_asset;
184                 dest_edit->nested_edl = dest_nested_edl;
185                 dest_edit->startproject = position + source_edit->startproject;
186
187
188
189 // Shift keyframes in source edit to their position in the
190 // destination edit for plugin case
191                 if(edit_autos) dest_edit->shift_keyframes(position);
192
193 // Shift following edits and keyframes in following edits by length
194 // in current source edit.
195                 for(Edit *future_edit = dest_edit->next;
196                         future_edit;
197                         future_edit = future_edit->next)
198                 {
199                         future_edit->startproject += dest_edit->length;
200                         future_edit->shift_keyframes(dest_edit->length);
201                 }
202
203                 source_len += source_edit->length;
204         }
205
206
207
208
209 // Fill remaining clipboard length with silence
210         if(source_len < min_length)
211         {
212 //printf("Edits::insert_edits %d\n", __LINE__);
213                 paste_silence(position + source_len, position + min_length);
214         }
215 }
216
217
218 // Native units
219 // Can't paste silence in here because it's used by paste_silence.
220 Edit* Edits::insert_new_edit(int64_t position)
221 {
222 //printf("Edits::insert_new_edit 1\n");
223         Edit *current = split_edit(position);
224
225 //printf("Edits::insert_new_edit 1\n");
226         Edit *new_edit = create_edit();
227         if( current ) current = PREVIOUS;
228 //printf("Edits::insert_new_edit 1\n");
229         insert_after(current, new_edit);
230         new_edit->startproject = position;
231 //printf("Edits::insert_new_edit 2\n");
232         return new_edit;
233 }
234
235 Edit* Edits::split_edit(int64_t position)
236 {
237 // Get edit containing position
238         Edit *edit = editof(position, PLAY_FORWARD, 0);
239         if(!edit) return 0;
240 // Split would have created a 0 length
241 //      if(edit->startproject == position) return edit;
242 // Create anyway so the return value comes before position
243
244         Edit *new_edit = create_edit();
245         insert_after(edit, new_edit);
246         new_edit->clone_from(edit);
247         new_edit->length = new_edit->startproject + new_edit->length - position;
248         edit->length = position - edit->startproject;
249         if( !new_edit->length || edit->silence() )
250                 new_edit->hard_left = new_edit->hard_right = 0;
251         else if( !edit->length )
252                 edit->hard_left = edit->hard_right = 0;
253         else {
254                 new_edit->hard_right = edit->hard_right;
255                 new_edit->hard_left = edit->hard_right = 0;
256         }
257         new_edit->startproject = position;
258         int64_t edit_start = edit->startproject;
259         int64_t edit_end = edit_start + edit->length;
260         new_edit->startsource += track->speed_length(edit_start, edit_end);
261
262 // Decide what to do with the transition
263         if(edit->length && edit->transition) {
264                 delete new_edit->transition;
265                 new_edit->transition = 0;
266         }
267
268         if(edit->transition && edit->transition->length > edit->length)
269                 edit->transition->length = edit->length;
270         if(new_edit->transition && new_edit->transition->length > new_edit->length)
271                 new_edit->transition->length = new_edit->length;
272         return new_edit;
273 }
274
275 int Edits::save(FileXML *xml, const char *output_path)
276 {
277         copy(0, length(), xml, output_path);
278         return 0;
279 }
280
281 void Edits::resample(double old_rate, double new_rate)
282 {
283         for(Edit *current = first; current; current = NEXT)
284         {
285                 current->startproject = Units::to_int64((double)current->startproject /
286                         old_rate *
287                         new_rate);
288                 if(PREVIOUS) PREVIOUS->length = current->startproject - PREVIOUS->startproject;
289                 current->startsource = Units::to_int64((double)current->startsource /
290                         old_rate *
291                         new_rate);
292                 if(!NEXT) current->length = Units::to_int64((double)current->length /
293                         old_rate *
294                         new_rate);
295                 if(current->transition)
296                 {
297                         current->transition->length = Units::to_int64(
298                                 (double)current->transition->length /
299                                 old_rate *
300                                 new_rate);
301                 }
302                 current->resample(old_rate, new_rate);
303         }
304 }
305
306 int Edits::is_glitch(Edit *edit)
307 {
308         if( track->data_type != TRACK_AUDIO ) return 0;
309         int64_t threshold = edl->session->frame_rate > 0 ?
310                 0.5 * edl->session->sample_rate / edl->session->frame_rate : 0;
311 // audio edit shorter than .5 frames is a glitch
312         return edit->length < threshold ? 1 : 0;
313 }
314
315 int Edits::optimize()
316 {
317         int result = 1;
318         Edit *current;
319
320 //printf("Edits::optimize %d\n", __LINE__);
321 // Sort edits by starting point
322         while(result)
323         {
324                 result = 0;
325
326                 for(current = first; current; current = NEXT)
327                 {
328                         Edit *next_edit = NEXT;
329
330                         if(next_edit && next_edit->startproject < current->startproject)
331                         {
332                                 swap(next_edit, current);
333                                 result = 1;
334                         }
335                 }
336         }
337
338 // trim edits before position 0
339         while( first && first->startproject+first->length < 0 )
340                 delete first;
341         if( first && first->startproject < 0 ) {
342                 first->length += first->startproject;
343                 first->startproject = 0;
344         }
345
346 // Insert silence between edits which aren't consecutive
347         for(current = last; current; current = current->previous)
348         {
349                 if(current->previous)
350                 {
351                         Edit *previous_edit = current->previous;
352                         if(current->startproject -
353                                 previous_edit->startproject -
354                                 previous_edit->length > 0)
355                         {
356                                 Edit *new_edit = create_edit();
357                                 insert_before(current, new_edit);
358                                 new_edit->startproject = previous_edit->startproject + previous_edit->length;
359                                 new_edit->length = current->startproject -
360                                         previous_edit->startproject -
361                                         previous_edit->length;
362                         }
363                 }
364                 else
365                 if(current->startproject > 0)
366                 {
367                         Edit *new_edit = create_edit();
368                         insert_before(current, new_edit);
369                         new_edit->length = current->startproject;
370                 }
371         }
372
373         result = 1;
374         while(result) {
375                 result = 0;
376
377 // delete 0 length edits
378                 for( current = first; !result && current; ) {
379                         Edit* prev = current->previous, *next = current->next;
380                         if( current->length == 0 ) {
381                                 if( next && current->transition && !next->transition) {
382                                         next->transition = current->transition;
383                                         next->transition->edit = next;
384                                         current->transition = 0;
385                                 }
386                                 if( !current->silence() ) {
387                                         if( current->hard_left && next && !next->silence() )
388                                                 next->hard_left = 1;
389                                         if( current->hard_right && prev && !prev->silence())
390                                                 prev->hard_right = 1;
391                                 }
392                                 delete current;
393                                 result = 1;
394                                 break;
395                         }
396                         current = next;
397                 }
398
399 // merge same files or transitions, and deglitch
400                 if( !result && track->data_type != TRACK_SUBTITLE ) {
401                         current = first;
402                         if( current && !current->hard_right &&
403                             current->next && !current->next->hard_left &&
404                             is_glitch(current) ) {
405 // if the first edit is a glitch, change it to silence
406                                 current->asset = 0;
407                                 current->nested_edl = 0;
408                         }
409                         Edit *next_edit = 0;
410                         for( ; current && (next_edit=current->next); current=NEXT ) {
411 // both edges are not hard edges
412                                 if( current->hard_right || next_edit->hard_left )
413                                         continue;
414 // next edit is a glitch
415                                 if( is_glitch(next_edit) )
416                                         break;
417 // both edits are silence & not a plugin
418                                 if( !current->is_plugin() && current->silence() &&
419                                     !next_edit->is_plugin() && next_edit->silence() )
420                                         break;
421 // source channels are identical & assets are identical
422                                 if( !result && current->channel == next_edit->channel &&
423                                     current->asset == next_edit->asset &&
424                                     current->nested_edl == next_edit->nested_edl ) {
425 //  and stop and start in the same frame
426                                         int64_t current_end = current->startsource + current->length;
427                                         int64_t next_start = next_edit->startsource;
428                                         if( current_end == next_start ||
429                                             EQUIV(edl->frame_align(track->from_units(current_end), 1),
430                                                   edl->frame_align(track->from_units(next_start), 1)) )
431                                                 break;
432                                 }
433                         }
434                         if( next_edit ) {
435                                 int64_t current_start = current->startproject;
436                                 int64_t next_end = next_edit->startproject + next_edit->length;
437                                 current->length = next_end - current_start;
438                                 current->hard_right = next_edit->hard_right;
439                                 remove(next_edit);
440                                 result = 1;
441                         }
442                 }
443
444                 if( last && last->silence() &&
445                     !last->transition && !last->hard_left && !last->hard_right ) {
446                         delete last;
447                         result = 1;
448                 }
449         }
450
451         return 0;
452 }
453
454
455 // ===================================== file operations
456
457 void Edits::load(FileXML *file, int track_offset)
458 {
459         int64_t startproject = 0;
460
461         while( last ) delete last;
462
463         while( !file->read_tag() ) {
464 //printf("Edits::load 1 %s\n", file->tag.get_title());
465                 if(!strcmp(file->tag.get_title(), "EDIT")) {
466                         load_edit(file, startproject, track_offset);
467                 }
468                 else if(!strcmp(file->tag.get_title(), "/EDITS"))
469                         break;
470         }
471
472 //track->dump();
473         optimize();
474 }
475
476 int Edits::load_edit(FileXML *file, int64_t &startproject, int track_offset)
477 {
478         Edit* current = append_new_edit();
479         current->load_properties(file, startproject);
480
481         startproject += current->length;
482
483         while( !file->read_tag() ) {
484                 if(file->tag.title_is("NESTED_EDL")) {
485                         char path[BCTEXTLEN];
486                         path[0] = 0;
487                         file->tag.get_property("SRC", path);
488 //printf("Edits::load_edit %d path=%s\n", __LINE__, path);
489                         if(path[0] != 0) {
490                                 current->nested_edl = edl->nested_edls.load(path);
491                         }
492 // printf("Edits::load_edit %d nested_edl->path=%s\n",
493 // __LINE__, current->nested_edl->path);
494                 }
495                 else if(file->tag.title_is("FILE")) {
496                         char filename[BCTEXTLEN];
497                         filename[0] = 0;
498                         file->tag.get_property("SRC", filename);
499 // Extend path
500                         if(filename[0] != 0) {
501                                 char directory[BCTEXTLEN], edl_directory[BCTEXTLEN];
502                                 FileSystem fs;
503                                 fs.set_current_dir("");
504                                 fs.extract_dir(directory, filename);
505                                 if(!strlen(directory)) {
506                                         fs.extract_dir(edl_directory, file->filename);
507                                         fs.join_names(directory, edl_directory, filename);
508                                         strcpy(filename, directory);
509                                 }
510                                 current->asset = edl->assets->get_asset(filename);
511                         }
512                         else {
513                                 current->asset = 0;
514                         }
515 //printf("Edits::load_edit 5\n");
516                 }
517                 else if(file->tag.title_is("TRANSITION")) {
518                         current->transition = new Transition(edl, current, "",
519                                 track->to_units(edl->session->default_transition_length, 1));
520                                 current->transition->load_xml(file);
521                 }
522                 else if(file->tag.title_is("/EDIT"))
523                         break;
524         }
525
526 //printf("Edits::load_edit %d\n", __LINE__);
527 //track->dump();
528 //printf("Edits::load_edit %d\n", __LINE__);
529         return 0;
530 }
531
532 // ============================================= accounting
533
534 int64_t Edits::length()
535 {
536         return last ? last->startproject + last->length : 0;
537 }
538
539
540
541 Edit* Edits::editof(int64_t position, int direction, int use_nudge)
542 {
543         Edit *current = 0;
544         if(use_nudge && track) position += track->nudge;
545
546         if(direction == PLAY_FORWARD) {
547                 for(current = last; current; current = PREVIOUS) {
548                         if(current->startproject <= position && current->startproject + current->length > position)
549                                 return current;
550                 }
551         }
552         else
553         if(direction == PLAY_REVERSE) {
554                 for(current = first; current; current = NEXT) {
555                         if(current->startproject < position && current->startproject + current->length >= position)
556                                 return current;
557                 }
558         }
559
560         return 0;     // return 0 on failure
561 }
562
563 Edit* Edits::get_edit(int id)
564 {
565         Edit *current = first;
566         while( current && current->orig_id != id ) current = NEXT;
567         return current;
568 }
569
570 Edit* Edits::get_playable_edit(int64_t position, int use_nudge)
571 {
572         Edit *current;
573         if(track && use_nudge) position += track->nudge;
574
575 // Get the current edit
576         for(current = first; current; current = NEXT) {
577                 if(current->startproject <= position &&
578                         current->startproject + current->length > position)
579                         break;
580         }
581
582 // Get the edit's asset
583 // TODO: descend into nested EDLs
584         if(current) {
585                 if(!current->asset)
586                         current = 0;
587         }
588
589         return current;     // return 0 on failure
590 }
591
592 // ================================================ editing
593
594
595
596 int Edits::copy(int64_t start, int64_t end, FileXML *file, const char *output_path)
597 {
598         Edit *current_edit;
599
600         file->tag.set_title("EDITS");
601         file->append_tag();
602         file->append_newline();
603
604         for(current_edit = first; current_edit; current_edit = current_edit->next)
605         {
606                 current_edit->copy(start, end, file, output_path);
607         }
608
609         file->tag.set_title("/EDITS");
610         file->append_tag();
611         file->append_newline();
612         return 0;
613 }
614
615
616
617 void Edits::clear(int64_t start, int64_t end)
618 {
619         if( start >= end ) return;
620
621         Edit* edit1 = editof(start, PLAY_FORWARD, 0);
622         Edit* edit2 = editof(end, PLAY_FORWARD, 0);
623         Edit* current_edit;
624
625         if(end == start) return;        // nothing selected
626         if(!edit1 && !edit2) return;       // nothing selected
627
628
629         if(!edit2) {                // edit2 beyond end of track
630                 edit2 = last;
631                 end = this->length();
632         }
633
634         if( edit1 && edit2 && edit1 != edit2)
635         {
636 // in different edits
637
638 //printf("Edits::clear 3.5 %d %d %d %d\n", edit1->startproject, edit1->length, edit2->startproject, edit2->length);
639                 edit1->length = start - edit1->startproject;
640                 edit2->length -= end - edit2->startproject;
641                 edit2->startsource += track->speed_length(edit2->startproject, end);
642                 edit2->startproject += end - edit2->startproject;
643
644 // delete
645                 for(current_edit = edit1->next; current_edit && current_edit != edit2;) {
646                         Edit* next = current_edit->next;
647                         remove(current_edit);
648                         current_edit = next;
649                 }
650 // shift
651                 for(current_edit = edit2; current_edit; current_edit = current_edit->next) {
652                         current_edit->startproject -= end - start;
653                 }
654         }
655         else {
656 // in same edit. paste_edit depends on this
657 // create a new edit
658                 current_edit = split_edit(start);
659                 if( current_edit ) {
660                         current_edit->length -= end - start;
661                         current_edit->startsource += track->speed_length(start, end);
662 // shift
663                         while( (current_edit=current_edit->next) != 0 ) {
664                                 current_edit->startproject -= end - start;
665                         }
666                 }
667         }
668
669         optimize();
670 }
671
672 // Used by edit handle and plugin handle movement but plugin handle movement
673 // can only effect other plugins.
674 void Edits::clear_recursive(int64_t start, int64_t end,
675         int edit_edits, int edit_labels, int edit_plugins, int edit_autos,
676         Edits *trim_edits)
677 {
678 //printf("Edits::clear_recursive 1\n");
679         track->clear(start, end,
680                 edit_edits, edit_labels, edit_plugins, edit_autos, trim_edits);
681 }
682
683
684 int Edits::clear_handle(double start, double end,
685         int edit_plugins, int edit_autos, double &distance)
686 {
687         Edit *current_edit;
688
689         distance = 0.0; // if nothing is found, distance is 0!
690         for(current_edit = first;
691                 current_edit && current_edit->next;
692                 current_edit = current_edit->next) {
693
694
695
696                 if(current_edit->asset && current_edit->next->asset) {
697
698                         if(current_edit->asset->equivalent(*current_edit->next->asset, 0, 0, edl)) {
699
700 // Got two consecutive edits in same source
701                                 if(edl->equivalent(track->from_units(current_edit->next->startproject),
702                                         start)) {
703 // handle selected
704                                         int length = -current_edit->length;
705                                         current_edit->length = current_edit->next->startsource - current_edit->startsource;
706                                         length += current_edit->length;
707
708 // Lengthen automation
709                                         if(edit_autos)
710                                                 track->automation->paste_silence(current_edit->next->startproject,
711                                                         current_edit->next->startproject + length);
712
713 // Lengthen effects
714                                         if(edit_plugins)
715                                                 track->shift_effects(current_edit->next->startproject,
716                                                                 length, edit_autos, 0);
717
718                                         for(current_edit = current_edit->next; current_edit; current_edit = current_edit->next)
719                                         {
720                                                 current_edit->startproject += length;
721                                         }
722
723                                         distance = track->from_units(length);
724                                         optimize();
725                                         break;
726                                 }
727                         }
728                 }
729         }
730
731         return 0;
732 }
733
734 int Edits::modify_handles(double oldposition, double newposition, int currentend,
735         int edit_mode, int edit_edits, int edit_labels, int edit_plugins, int edit_autos,
736         Edits *trim_edits, int group_id)
737 {
738         int result = 0;
739         Edit *current_edit;
740         Edit *left = 0, *right = 0;
741         if( group_id > 0 ) {
742                 double start = DBL_MAX, end = DBL_MIN;
743                 for( Edit *edit=first; edit; edit=edit->next ) {
744                         if( edit->group_id != group_id ) continue;
745                         double edit_start = edit->track->from_units(edit->startproject);
746                         if( edit_start < start ) { start = edit_start;  left = edit; }
747                         double edit_end = edit->track->from_units(edit->startproject+edit->length);
748                         if( edit_end > end ) { end = edit_end;  right = edit; }
749                 }
750         }
751
752 //printf("Edits::modify_handles 1 %d %f %f\n", currentend, newposition, oldposition);
753         if(currentend == 0) {
754 // left handle
755                 for(current_edit = first; current_edit && !result;) {
756                         if( group_id > 0 ? current_edit == left :
757                             edl->equivalent(track->from_units(current_edit->startproject),
758                                 oldposition) ) {
759 // edit matches selection
760 //printf("Edits::modify_handles 3 %f %f\n", newposition, oldposition);
761                                 double delta = newposition - oldposition;
762                                 oldposition = track->from_units(current_edit->startproject);
763                                 if( group_id > 0 ) newposition = oldposition + delta;
764                                 current_edit->shift_start(edit_mode,
765                                         track->to_units(newposition, 0), track->to_units(oldposition, 0),
766                                         edit_labels, edit_autos, edit_plugins, trim_edits);
767                                 result = 1;
768                         }
769
770                         if(!result) current_edit = current_edit->next;
771                 }
772         }
773         else {
774 // right handle selected
775                 for(current_edit = first; current_edit && !result;) {
776                         if( group_id > 0 ? current_edit == right :
777                             edl->equivalent(track->from_units(current_edit->startproject) +
778                                         track->from_units(current_edit->length), oldposition) ) {
779                                 double delta = newposition - oldposition;
780                                 oldposition = track->from_units(current_edit->startproject) +
781                                         track->from_units(current_edit->length);
782                                 if( group_id > 0 ) newposition = oldposition + delta;
783                                 result = 1;
784
785                                 current_edit->shift_end(edit_mode,
786                                         track->to_units(newposition, 0), track->to_units(oldposition, 0),
787                                         edit_labels, edit_autos, edit_plugins, trim_edits);
788                         }
789
790                         if(!result) current_edit = current_edit->next;
791 //printf("Edits::modify_handle 8\n");
792                 }
793         }
794
795         optimize();
796         return 0;
797 }
798
799 void Edits::paste_silence(int64_t start, int64_t end)
800 {
801         Edit *new_edit = editof(start, PLAY_FORWARD, 0);
802         if (!new_edit) return;
803
804         if( !new_edit->silence() || new_edit->hard_right ) {
805                 new_edit = insert_new_edit(start);
806                 new_edit->length = end - start;
807         }
808         else
809                 new_edit->length += end - start;
810
811         for(Edit *current = new_edit->next; current; current = NEXT) {
812                 current->startproject += end - start;
813         }
814
815         return;
816 }
817
818 Edit *Edits::create_silence(int64_t start, int64_t end)
819 {
820         Edit *new_edit = insert_new_edit(start);
821         new_edit->length = end - start;
822         for(Edit *current = new_edit->next; current; current = NEXT) {
823                 current->startproject += end - start;
824         }
825         return new_edit;
826 }
827
828 Edit* Edits::shift(int64_t position, int64_t difference)
829 {
830         Edit *new_edit = split_edit(position);
831
832         for(Edit *current = first; current; current = NEXT) {
833                 if(current->startproject >= position) {
834                         current->shift(difference);
835                 }
836         }
837         return new_edit;
838 }
839
840
841 void Edits::shift_keyframes_recursive(int64_t position, int64_t length)
842 {
843         track->shift_keyframes(position, length);
844 }
845
846 void Edits::shift_effects_recursive(int64_t position, int64_t length, int edit_autos)
847 {
848         track->shift_effects(position, length, edit_autos, 0);
849 }
850
851 double Edits::early_timecode()
852 {
853         double result = -1;
854         for( Edit *edit=first; edit; edit=edit->next ) {
855                 Asset *asset = edit->asset;
856                 if( !asset ) continue;
857                 if( asset->timecode < -1 )
858                         asset->timecode = FFMPEG::get_timecode(asset->path,
859                                 track->data_type, edit->channel,
860                                 edl->session->frame_rate);
861                 if( asset->timecode < 0 ) continue;
862                 if( result < 0 ||  result > asset->timecode )
863                         result = asset->timecode;
864         }
865         return result;
866 }
867
868 void Edits::align_timecodes(double offset)
869 {
870         for( Edit *edit=first, *next=0; edit; edit=next ) {
871                 next = edit->next;
872                 if( edit->silence() ) delete edit;
873         }
874         for( Edit *edit=first, *next=0; edit; edit=next ) {
875                 next = edit->next;
876                 Asset *asset = edit->asset;
877                 if( !asset && asset->timecode < 0 ) continue;
878                 double position = asset->timecode - offset;
879                 edit->startproject = track->to_units(position, 1) + edit->startsource;
880         }
881         int result = 1;
882         while( result ) {
883                 result = 0;
884                 for( Edit *edit=first, *next=0; edit; edit=next ) {
885                         next = edit->next;
886                         if( !next || next->startproject >= edit->startproject ) continue;
887                         swap(next, edit);
888                         next = edit;
889                         result = 1;
890                 }
891         }
892         int64_t startproject = 0;
893         for( Edit *edit=first, *next=0; edit; edit=next ) {
894                 if( (next = edit->next) != 0 ) {
895                         int64_t length = next->startproject - startproject;
896                         if( length > edit->length ) edit->length = length;
897                 }
898                 int64_t length = edit->startproject - startproject;
899                 if( length > 0 ) {
900                         Edit *new_edit = create_edit();
901                         insert_before(edit, new_edit);
902                         new_edit->startproject = startproject;
903                         new_edit->length = length;
904                         startproject = edit->startproject;
905                 }
906                 startproject += edit->length;
907         }
908 }
909
910 void Edits::update_idxbl_length(int id, int64_t du)
911 {
912         for( Edit *edit=first; edit; edit=edit->next ) {
913                 Indexable *idxbl = edit->asset ? (Indexable *)edit->asset :
914                         edit->nested_edl ? (Indexable *)edit->nested_edl : 0;
915                 if( !idxbl || idxbl->id != id ) continue;
916                 edit->length += du;
917                 if(  edit->length > 0 && edit->next ) {
918                         int64_t next_start = edit->next->startproject;
919                         int64_t edit_end = edit->startproject + edit->length;
920                         if( edit_end > next_start )
921                                 edit->length = next_start - edit->startproject;
922                 }
923                 if( edit->length < 0 ) edit->length = 0;
924         }
925 }
926