4 * Copyright (C) 2008-2017 Adam Williams <broadcast at earthling dot net>
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.
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.
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
25 #include "automation.h"
26 #include "bcsignals.h"
33 #include "edlsession.h"
37 #include "filesystem.h"
38 #include "localsession.h"
40 #include "strategies.inc"
42 #include "transition.h"
43 #include "transportque.inc"
47 Edits::Edits(EDL *edl, Track *track)
59 void Edits::equivalent_output(Edits *edits, int64_t *result)
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;
67 that_current = that_current->next)
69 //printf("Edits::equivalent_output 1 %d\n", *result);
70 if(!current && that_current)
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);
79 if(current && !that_current)
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);
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);
96 void Edits::copy_from(Edits *edits)
98 while(last) delete last;
99 for(Edit *current = edits->first; current; current = NEXT)
101 Edit *new_edit = append(create_edit());
102 new_edit->copy_from(current);
107 Edits& Edits::operator=(Edits& edits)
109 printf("Edits::operator= 1\n");
115 void Edits::insert_asset(Asset *asset, EDL *nested_edl,
116 int64_t length, int64_t position, int track_number)
118 Edit *new_edit = insert_new_edit(position);
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;
128 if(track->data_type == TRACK_AUDIO)
129 new_edit->channel = track_number % nested_edl->session->audio_channels;
131 new_edit->channel = 0;
134 if(asset && !nested_edl)
136 if(asset->audio_data)
137 new_edit->channel = track_number % asset->channels;
139 if(asset->video_data)
140 new_edit->channel = track_number % asset->layers;
143 //printf("Edits::insert_asset %d %d\n", new_edit->channel, new_edit->length);
144 for(Edit *current = new_edit->next; current; current = NEXT)
146 current->startproject += length;
150 void Edits::insert_edits(Edits *source_edits,
155 //int64_t clipboard_end = position + min_length;
156 // Length pasted so far
157 int64_t source_len = 0;
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())
163 paste_silence(length(), position);
167 for(Edit *source_edit = source_edits->first;
169 source_edit = source_edit->next)
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);
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);
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;
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);
193 // Shift following edits and keyframes in following edits by length
194 // in current source edit.
195 for(Edit *future_edit = dest_edit->next;
197 future_edit = future_edit->next)
199 future_edit->startproject += dest_edit->length;
200 future_edit->shift_keyframes(dest_edit->length);
203 source_len += source_edit->length;
209 // Fill remaining clipboard length with silence
210 if(source_len < min_length)
212 //printf("Edits::insert_edits %d\n", __LINE__);
213 paste_silence(position + source_len, position + min_length);
219 // Can't paste silence in here because it's used by paste_silence.
220 Edit* Edits::insert_new_edit(int64_t position)
222 //printf("Edits::insert_new_edit 1\n");
223 Edit *current = split_edit(position);
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");
235 Edit* Edits::split_edit(int64_t position)
237 // Get edit containing position
238 Edit *edit = editof(position, PLAY_FORWARD, 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
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;
254 new_edit->hard_right = edit->hard_right;
255 new_edit->hard_left = edit->hard_right = 0;
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);
262 // Decide what to do with the transition
263 if(edit->length && edit->transition) {
264 delete new_edit->transition;
265 new_edit->transition = 0;
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;
275 int Edits::save(FileXML *xml, const char *output_path)
277 copy(0, length(), xml, output_path);
281 void Edits::resample(double old_rate, double new_rate)
283 for(Edit *current = first; current; current = NEXT)
285 current->startproject = Units::to_int64((double)current->startproject /
288 if(PREVIOUS) PREVIOUS->length = current->startproject - PREVIOUS->startproject;
289 current->startsource = Units::to_int64((double)current->startsource /
292 if(!NEXT) current->length = Units::to_int64((double)current->length /
295 if(current->transition)
297 current->transition->length = Units::to_int64(
298 (double)current->transition->length /
302 current->resample(old_rate, new_rate);
306 int Edits::is_glitch(Edit *edit)
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;
315 int Edits::optimize()
320 //printf("Edits::optimize %d\n", __LINE__);
321 // Sort edits by starting point
326 for(current = first; current; current = NEXT)
328 Edit *next_edit = NEXT;
330 if(next_edit && next_edit->startproject < current->startproject)
332 swap(next_edit, current);
338 // trim edits before position 0
339 while( first && first->startproject+first->length < 0 )
341 if( first && first->startproject < 0 ) {
342 first->length += first->startproject;
343 first->startproject = 0;
346 // Insert silence between edits which aren't consecutive
347 for(current = last; current; current = current->previous)
349 if(current->previous)
351 Edit *previous_edit = current->previous;
352 if(current->startproject -
353 previous_edit->startproject -
354 previous_edit->length > 0)
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;
365 if(current->startproject > 0)
367 Edit *new_edit = create_edit();
368 insert_before(current, new_edit);
369 new_edit->length = current->startproject;
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;
386 if( !current->silence() ) {
387 if( current->hard_left && next && !next->silence() )
389 if( current->hard_right && prev && !prev->silence())
390 prev->hard_right = 1;
399 // merge same files or transitions, and deglitch
400 if( !result && track->data_type != TRACK_SUBTITLE ) {
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
407 current->nested_edl = 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 )
414 // next edit is a glitch
415 if( is_glitch(next_edit) )
417 // both edits are silence & not a plugin
418 if( !current->is_plugin() && current->silence() &&
419 !next_edit->is_plugin() && next_edit->silence() )
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)) )
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;
444 if( last && last->silence() &&
445 !last->transition && !last->hard_left && !last->hard_right ) {
455 // ===================================== file operations
457 void Edits::load(FileXML *file, int track_offset)
459 int64_t startproject = 0;
461 while( last ) delete last;
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);
468 else if(!strcmp(file->tag.get_title(), "/EDITS"))
476 int Edits::load_edit(FileXML *file, int64_t &startproject, int track_offset)
478 Edit* current = append_new_edit();
479 current->load_properties(file, startproject);
481 startproject += current->length;
483 while( !file->read_tag() ) {
484 if(file->tag.title_is("NESTED_EDL")) {
485 char path[BCTEXTLEN];
487 file->tag.get_property("SRC", path);
488 //printf("Edits::load_edit %d path=%s\n", __LINE__, path);
490 current->nested_edl = edl->nested_edls.load(path);
492 // printf("Edits::load_edit %d nested_edl->path=%s\n",
493 // __LINE__, current->nested_edl->path);
495 else if(file->tag.title_is("FILE")) {
496 char filename[BCTEXTLEN];
498 file->tag.get_property("SRC", filename);
500 if(filename[0] != 0) {
501 char directory[BCTEXTLEN], edl_directory[BCTEXTLEN];
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);
510 current->asset = edl->assets->get_asset(filename);
515 //printf("Edits::load_edit 5\n");
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);
522 else if(file->tag.title_is("/EDIT"))
526 //printf("Edits::load_edit %d\n", __LINE__);
528 //printf("Edits::load_edit %d\n", __LINE__);
532 // ============================================= accounting
534 int64_t Edits::length()
536 return last ? last->startproject + last->length : 0;
541 Edit* Edits::editof(int64_t position, int direction, int use_nudge)
544 if(use_nudge && track) position += track->nudge;
546 if(direction == PLAY_FORWARD) {
547 for(current = last; current; current = PREVIOUS) {
548 if(current->startproject <= position && current->startproject + current->length > position)
553 if(direction == PLAY_REVERSE) {
554 for(current = first; current; current = NEXT) {
555 if(current->startproject < position && current->startproject + current->length >= position)
560 return 0; // return 0 on failure
563 Edit* Edits::get_edit(int id)
565 Edit *current = first;
566 while( current && current->orig_id != id ) current = NEXT;
570 Edit* Edits::get_playable_edit(int64_t position, int use_nudge)
573 if(track && use_nudge) position += track->nudge;
575 // Get the current edit
576 for(current = first; current; current = NEXT) {
577 if(current->startproject <= position &&
578 current->startproject + current->length > position)
582 // Get the edit's asset
583 // TODO: descend into nested EDLs
589 return current; // return 0 on failure
592 // ================================================ editing
596 int Edits::copy(int64_t start, int64_t end, FileXML *file, const char *output_path)
600 file->tag.set_title("EDITS");
602 file->append_newline();
604 for(current_edit = first; current_edit; current_edit = current_edit->next)
606 current_edit->copy(start, end, file, output_path);
609 file->tag.set_title("/EDITS");
611 file->append_newline();
617 void Edits::clear(int64_t start, int64_t end)
619 if( start >= end ) return;
621 Edit* edit1 = editof(start, PLAY_FORWARD, 0);
622 Edit* edit2 = editof(end, PLAY_FORWARD, 0);
625 if(end == start) return; // nothing selected
626 if(!edit1 && !edit2) return; // nothing selected
629 if(!edit2) { // edit2 beyond end of track
631 end = this->length();
634 if( edit1 && edit2 && edit1 != edit2)
636 // in different edits
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;
645 for(current_edit = edit1->next; current_edit && current_edit != edit2;) {
646 Edit* next = current_edit->next;
647 remove(current_edit);
651 for(current_edit = edit2; current_edit; current_edit = current_edit->next) {
652 current_edit->startproject -= end - start;
656 // in same edit. paste_edit depends on this
658 current_edit = split_edit(start);
660 current_edit->length -= end - start;
661 current_edit->startsource += track->speed_length(start, end);
663 while( (current_edit=current_edit->next) != 0 ) {
664 current_edit->startproject -= end - start;
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,
678 //printf("Edits::clear_recursive 1\n");
679 track->clear(start, end,
680 edit_edits, edit_labels, edit_plugins, edit_autos, trim_edits);
684 int Edits::clear_handle(double start, double end,
685 int edit_plugins, int edit_autos, double &distance)
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) {
696 if(current_edit->asset && current_edit->next->asset) {
698 if(current_edit->asset->equivalent(*current_edit->next->asset, 0, 0, edl)) {
700 // Got two consecutive edits in same source
701 if(edl->equivalent(track->from_units(current_edit->next->startproject),
704 int length = -current_edit->length;
705 current_edit->length = current_edit->next->startsource - current_edit->startsource;
706 length += current_edit->length;
708 // Lengthen automation
710 track->automation->paste_silence(current_edit->next->startproject,
711 current_edit->next->startproject + length);
715 track->shift_effects(current_edit->next->startproject,
716 length, edit_autos, 0);
718 for(current_edit = current_edit->next; current_edit; current_edit = current_edit->next)
720 current_edit->startproject += length;
723 distance = track->from_units(length);
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)
740 Edit *left = 0, *right = 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; }
752 //printf("Edits::modify_handles 1 %d %f %f\n", currentend, newposition, oldposition);
753 if(currentend == 0) {
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),
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);
770 if(!result) current_edit = current_edit->next;
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;
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);
790 if(!result) current_edit = current_edit->next;
791 //printf("Edits::modify_handle 8\n");
799 void Edits::paste_silence(int64_t start, int64_t end)
801 Edit *new_edit = editof(start, PLAY_FORWARD, 0);
802 if (!new_edit) return;
804 if( !new_edit->silence() || new_edit->hard_right ) {
805 new_edit = insert_new_edit(start);
806 new_edit->length = end - start;
809 new_edit->length += end - start;
811 for(Edit *current = new_edit->next; current; current = NEXT) {
812 current->startproject += end - start;
818 Edit *Edits::create_silence(int64_t start, int64_t end)
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;
828 Edit* Edits::shift(int64_t position, int64_t difference)
830 Edit *new_edit = split_edit(position);
832 for(Edit *current = first; current; current = NEXT) {
833 if(current->startproject >= position) {
834 current->shift(difference);
841 void Edits::shift_keyframes_recursive(int64_t position, int64_t length)
843 track->shift_keyframes(position, length);
846 void Edits::shift_effects_recursive(int64_t position, int64_t length, int edit_autos)
848 track->shift_effects(position, length, edit_autos, 0);
851 double Edits::early_timecode()
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;
868 void Edits::align_timecodes(double offset)
870 for( Edit *edit=first, *next=0; edit; edit=next ) {
872 if( edit->silence() ) delete edit;
874 for( Edit *edit=first, *next=0; edit; 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;
884 for( Edit *edit=first, *next=0; edit; edit=next ) {
886 if( !next || next->startproject >= edit->startproject ) continue;
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;
898 int64_t length = edit->startproject - startproject;
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;
906 startproject += edit->length;
910 void Edits::update_idxbl_length(int id, int64_t du)
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;
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;
923 if( edit->length < 0 ) edit->length = 0;