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"
36 #include "filesystem.h"
37 #include "localsession.h"
39 #include "strategies.inc"
41 #include "transition.h"
42 #include "transportque.inc"
46 Edits::Edits(EDL *edl, Track *track)
58 void Edits::equivalent_output(Edits *edits, int64_t *result)
60 // For the case of plugin sets, a new plugin set may be created with
61 // plugins only starting after 0. We only want to restart brender at
62 // the first plugin in this case.
63 for(Edit *current = first, *that_current = edits->first;
64 current || that_current;
66 that_current = that_current->next)
68 //printf("Edits::equivalent_output 1 %d\n", *result);
69 if(!current && that_current)
71 int64_t position1 = length();
72 int64_t position2 = that_current->startproject;
73 if(*result < 0 || *result > MIN(position1, position2))
74 *result = MIN(position1, position2);
78 if(current && !that_current)
80 int64_t position1 = edits->length();
81 int64_t position2 = current->startproject;
82 if(*result < 0 || *result > MIN(position1, position2))
83 *result = MIN(position1, position2);
88 //printf("Edits::equivalent_output 2 %d\n", *result);
89 current->equivalent_output(that_current, result);
90 //printf("Edits::equivalent_output 3 %d\n", *result);
95 void Edits::copy_from(Edits *edits)
97 while(last) delete last;
98 for(Edit *current = edits->first; current; current = NEXT)
100 Edit *new_edit = append(create_edit());
101 new_edit->copy_from(current);
106 Edits& Edits::operator=(Edits& edits)
108 printf("Edits::operator= 1\n");
114 void Edits::insert_asset(Asset *asset, EDL *nested_edl,
115 int64_t length, int64_t position, int track_number)
117 Edit *new_edit = insert_new_edit(position);
119 new_edit->nested_edl = nested_edl;
120 new_edit->asset = asset;
121 new_edit->startsource = 0;
122 new_edit->startproject = position;
123 new_edit->length = length;
127 if(track->data_type == TRACK_AUDIO)
128 new_edit->channel = track_number % nested_edl->session->audio_channels;
130 new_edit->channel = 0;
133 if(asset && !nested_edl)
135 if(asset->audio_data)
136 new_edit->channel = track_number % asset->channels;
138 if(asset->video_data)
139 new_edit->channel = track_number % asset->layers;
142 //printf("Edits::insert_asset %d %d\n", new_edit->channel, new_edit->length);
143 for(Edit *current = new_edit->next; current; current = NEXT)
145 current->startproject += length;
149 void Edits::insert_edits(Edits *source_edits,
154 //int64_t clipboard_end = position + min_length;
155 // Length pasted so far
156 int64_t source_len = 0;
158 // Fill region between end of edit table and beginning of pasted segment
159 // with silence. Can't call from insert_new_edit because it's recursive.
160 if(position > length())
162 paste_silence(length(), position);
166 for(Edit *source_edit = source_edits->first;
168 source_edit = source_edit->next)
170 EDL *dest_nested_edl = 0;
171 if(source_edit->nested_edl)
172 dest_nested_edl = edl->nested_edls.get_nested(source_edit->nested_edl);
175 Asset *dest_asset = 0;
176 if(source_edit->asset)
177 dest_asset = edl->assets->update(source_edit->asset);
178 // Open destination area
179 Edit *dest_edit = insert_new_edit(position + source_edit->startproject);
181 dest_edit->copy_from(source_edit);
182 dest_edit->asset = dest_asset;
183 dest_edit->nested_edl = dest_nested_edl;
184 dest_edit->startproject = position + source_edit->startproject;
188 // Shift keyframes in source edit to their position in the
189 // destination edit for plugin case
190 if(edit_autos) dest_edit->shift_keyframes(position);
192 // Shift following edits and keyframes in following edits by length
193 // in current source edit.
194 for(Edit *future_edit = dest_edit->next;
196 future_edit = future_edit->next)
198 future_edit->startproject += dest_edit->length;
199 future_edit->shift_keyframes(dest_edit->length);
202 source_len += source_edit->length;
208 // Fill remaining clipboard length with silence
209 if(source_len < min_length)
211 //printf("Edits::insert_edits %d\n", __LINE__);
212 paste_silence(position + source_len, position + min_length);
218 // Can't paste silence in here because it's used by paste_silence.
219 Edit* Edits::insert_new_edit(int64_t position)
221 //printf("Edits::insert_new_edit 1\n");
222 Edit *current = split_edit(position);
223 if(current) current = PREVIOUS;
225 //printf("Edits::insert_new_edit 1\n");
226 Edit *new_edit = create_edit();
227 //printf("Edits::insert_new_edit 1\n");
228 insert_after(current, new_edit);
229 new_edit->startproject = position;
230 //printf("Edits::insert_new_edit 2\n");
234 Edit* Edits::split_edit(int64_t position)
236 // Get edit containing position
237 Edit *edit = editof(position, PLAY_FORWARD, 0);
239 // Split would have created a 0 length
240 // if(edit->startproject == position) return edit;
241 // Create anyway so the return value comes before position
243 Edit *new_edit = create_edit();
244 insert_after(edit, new_edit);
245 new_edit->copy_from(edit);
246 new_edit->length = new_edit->startproject + new_edit->length - position;
247 edit->length = position - edit->startproject;
248 new_edit->startproject = position;
249 new_edit->startsource += edit->length;
251 // Decide what to do with the transition
252 if(edit->length && edit->transition) {
253 delete new_edit->transition;
254 new_edit->transition = 0;
257 if(edit->transition && edit->transition->length > edit->length)
258 edit->transition->length = edit->length;
259 if(new_edit->transition && new_edit->transition->length > new_edit->length)
260 new_edit->transition->length = new_edit->length;
264 int Edits::save(FileXML *xml, const char *output_path)
266 copy(0, length(), xml, output_path);
270 void Edits::resample(double old_rate, double new_rate)
272 for(Edit *current = first; current; current = NEXT)
274 current->startproject = Units::to_int64((double)current->startproject /
277 if(PREVIOUS) PREVIOUS->length = current->startproject - PREVIOUS->startproject;
278 current->startsource = Units::to_int64((double)current->startsource /
281 if(!NEXT) current->length = Units::to_int64((double)current->length /
284 if(current->transition)
286 current->transition->length = Units::to_int64(
287 (double)current->transition->length /
291 current->resample(old_rate, new_rate);
295 int Edits::is_glitch(Edit *edit)
297 if( track->data_type != TRACK_AUDIO ) return 0;
298 int64_t threshold = edl->session->frame_rate > 0 ?
299 0.5 * edl->session->sample_rate / edl->session->frame_rate : 0;
300 // audio edit shorter than .5 frames is a glitch
301 return edit->length < threshold ? 1 : 0;
304 int Edits::optimize()
309 //printf("Edits::optimize %d\n", __LINE__);
310 // Sort edits by starting point
315 for(current = first; current; current = NEXT)
317 Edit *next_edit = NEXT;
319 if(next_edit && next_edit->startproject < current->startproject)
321 swap(next_edit, current);
327 // Insert silence between edits which aren't consecutive
328 for(current = last; current; current = current->previous)
330 if(current->previous)
332 Edit *previous_edit = current->previous;
333 if(current->startproject -
334 previous_edit->startproject -
335 previous_edit->length > 0)
337 Edit *new_edit = create_edit();
338 insert_before(current, new_edit);
339 new_edit->startproject = previous_edit->startproject + previous_edit->length;
340 new_edit->length = current->startproject -
341 previous_edit->startproject -
342 previous_edit->length;
346 if(current->startproject > 0)
348 Edit *new_edit = create_edit();
349 insert_before(current, new_edit);
350 new_edit->length = current->startproject;
358 // delete 0 length edits
359 for( current = first; !result && current; ) {
360 Edit* next = current->next;
361 if( current->length == 0 ) {
362 if( next && current->transition && !next->transition) {
363 next->transition = current->transition;
364 next->transition->edit = next;
365 current->transition = 0;
374 // merge same files or transitions, and deglitch
375 if( !result && track->data_type != TRACK_SUBTITLE ) {
377 if( current && !current->hard_right &&
378 current->next && !current->next->hard_left &&
379 is_glitch(current) ) {
380 // if the first edit is a glitch, change it to silence
382 current->nested_edl = 0;
385 for( ; current && (next_edit=current->next); current=NEXT ) {
386 // both edges are not hard edges
387 if( current->hard_right || next_edit->hard_left ) continue;
388 // next edit is a glitch
389 if( is_glitch(next_edit) )
391 // both edits are silence & not a plugin
392 if( !current->is_plugin && current->silence() &&
393 !next_edit->is_plugin && next_edit->silence() )
395 // source channels are identical & assets are identical
396 if( !result && current->channel == next_edit->channel &&
397 current->asset == next_edit->asset &&
398 current->nested_edl == next_edit->nested_edl ) {
399 // and stop and start in the same frame
400 int64_t current_end = current->startsource + current->length;
401 int64_t next_start = next_edit->startsource;
402 if( current_end == next_start ||
403 EQUIV(edl->frame_align(track->from_units(current_end), 1),
404 edl->frame_align(track->from_units(next_start), 1)) )
409 int64_t current_start = current->startproject;
410 int64_t next_end = next_edit->startproject + next_edit->length;
411 current->length = next_end - current_start;
417 if( last && last->silence() && !last->transition ) {
427 // ===================================== file operations
429 void Edits::load(FileXML *file, int track_offset)
431 int64_t startproject = 0;
433 while( last ) delete last;
435 while( !file->read_tag() ) {
436 //printf("Edits::load 1 %s\n", file->tag.get_title());
437 if(!strcmp(file->tag.get_title(), "EDIT")) {
438 load_edit(file, startproject, track_offset);
440 else if(!strcmp(file->tag.get_title(), "/EDITS"))
448 int Edits::load_edit(FileXML *file, int64_t &startproject, int track_offset)
450 Edit* current = append_new_edit();
451 current->load_properties(file, startproject);
453 startproject += current->length;
455 while( !file->read_tag() ) {
456 if(file->tag.title_is("NESTED_EDL")) {
457 char path[BCTEXTLEN];
459 file->tag.get_property("SRC", path);
460 //printf("Edits::load_edit %d path=%s\n", __LINE__, path);
462 current->nested_edl = edl->nested_edls.load(path);
464 // printf("Edits::load_edit %d nested_edl->path=%s\n",
465 // __LINE__, current->nested_edl->path);
467 else if(file->tag.title_is("FILE")) {
468 char filename[BCTEXTLEN];
470 file->tag.get_property("SRC", filename);
472 if(filename[0] != 0) {
473 char directory[BCTEXTLEN], edl_directory[BCTEXTLEN];
475 fs.set_current_dir("");
476 fs.extract_dir(directory, filename);
477 if(!strlen(directory)) {
478 fs.extract_dir(edl_directory, file->filename);
479 fs.join_names(directory, edl_directory, filename);
480 strcpy(filename, directory);
482 current->asset = edl->assets->get_asset(filename);
487 //printf("Edits::load_edit 5\n");
489 else if(file->tag.title_is("TRANSITION")) {
490 current->transition = new Transition(edl, current, "",
491 track->to_units(edl->session->default_transition_length, 1));
492 current->transition->load_xml(file);
494 else if(file->tag.title_is("/EDIT"))
498 //printf("Edits::load_edit %d\n", __LINE__);
500 //printf("Edits::load_edit %d\n", __LINE__);
504 // ============================================= accounting
506 int64_t Edits::length()
508 return last ? last->startproject + last->length : 0;
513 Edit* Edits::editof(int64_t position, int direction, int use_nudge)
516 if(use_nudge && track) position += track->nudge;
518 if(direction == PLAY_FORWARD) {
519 for(current = last; current; current = PREVIOUS) {
520 if(current->startproject <= position && current->startproject + current->length > position)
525 if(direction == PLAY_REVERSE) {
526 for(current = first; current; current = NEXT) {
527 if(current->startproject < position && current->startproject + current->length >= position)
532 return 0; // return 0 on failure
535 Edit* Edits::get_playable_edit(int64_t position, int use_nudge)
538 if(track && use_nudge) position += track->nudge;
540 // Get the current edit
541 for(current = first; current; current = NEXT) {
542 if(current->startproject <= position &&
543 current->startproject + current->length > position)
547 // Get the edit's asset
548 // TODO: descend into nested EDLs
554 return current; // return 0 on failure
557 // ================================================ editing
561 int Edits::copy(int64_t start, int64_t end, FileXML *file, const char *output_path)
565 file->tag.set_title("EDITS");
567 file->append_newline();
569 for(current_edit = first; current_edit; current_edit = current_edit->next)
571 current_edit->copy(start, end, file, output_path);
574 file->tag.set_title("/EDITS");
576 file->append_newline();
582 void Edits::clear(int64_t start, int64_t end)
584 if( start >= end ) return;
586 Edit* edit1 = editof(start, PLAY_FORWARD, 0);
587 Edit* edit2 = editof(end, PLAY_FORWARD, 0);
590 if(end == start) return; // nothing selected
591 if(!edit1 && !edit2) return; // nothing selected
594 if(!edit2) { // edit2 beyond end of track
596 end = this->length();
599 if( edit1 && edit2 && edit1 != edit2)
601 // in different edits
603 //printf("Edits::clear 3.5 %d %d %d %d\n", edit1->startproject, edit1->length, edit2->startproject, edit2->length);
604 edit1->length = start - edit1->startproject;
605 edit2->length -= end - edit2->startproject;
606 edit2->startsource += end - edit2->startproject;
607 edit2->startproject += end - edit2->startproject;
610 for(current_edit = edit1->next; current_edit && current_edit != edit2;) {
611 Edit* next = current_edit->next;
612 remove(current_edit);
616 for(current_edit = edit2; current_edit; current_edit = current_edit->next) {
617 current_edit->startproject -= end - start;
621 // in same edit. paste_edit depends on this
623 current_edit = split_edit(start);
625 current_edit->length -= end - start;
626 current_edit->startsource += end - start;
628 while( (current_edit=current_edit->next) != 0 ) {
629 current_edit->startproject -= end - start;
637 // Used by edit handle and plugin handle movement but plugin handle movement
638 // can only effect other plugins.
639 void Edits::clear_recursive(int64_t start, int64_t end,
640 int edit_edits, int edit_labels, int edit_plugins, int edit_autos,
643 //printf("Edits::clear_recursive 1\n");
644 track->clear(start, end,
645 edit_edits, edit_labels, edit_plugins, edit_autos, 0,
650 int Edits::clear_handle(double start, double end,
651 int edit_plugins, int edit_autos, double &distance)
655 distance = 0.0; // if nothing is found, distance is 0!
656 for(current_edit = first;
657 current_edit && current_edit->next;
658 current_edit = current_edit->next) {
662 if(current_edit->asset && current_edit->next->asset) {
664 if(current_edit->asset->equivalent(*current_edit->next->asset, 0, 0, edl)) {
666 // Got two consecutive edits in same source
667 if(edl->equivalent(track->from_units(current_edit->next->startproject),
670 int length = -current_edit->length;
671 current_edit->length = current_edit->next->startsource - current_edit->startsource;
672 length += current_edit->length;
674 // Lengthen automation
676 track->automation->paste_silence(current_edit->next->startproject,
677 current_edit->next->startproject + length);
681 track->shift_effects(current_edit->next->startproject,
685 for(current_edit = current_edit->next; current_edit; current_edit = current_edit->next)
687 current_edit->startproject += length;
690 distance = track->from_units(length);
701 int Edits::modify_handles(double oldposition, double newposition, int currentend,
702 int edit_mode, int edit_edits, int edit_labels, int edit_plugins, int edit_autos,
708 //printf("Edits::modify_handles 1 %d %f %f\n", currentend, newposition, oldposition);
709 if(currentend == 0) {
711 for(current_edit = first; current_edit && !result;) {
712 if(edl->equivalent(track->from_units(current_edit->startproject),
714 // edit matches selection
715 //printf("Edits::modify_handles 3 %f %f\n", newposition, oldposition);
716 oldposition = track->from_units(current_edit->startproject);
719 if(newposition >= oldposition) {
720 //printf("Edits::modify_handle 1 %s %f %f\n", track->title, oldposition, newposition);
721 // shift start of edit in
722 current_edit->shift_start_in(edit_mode,
723 track->to_units(newposition, 0),
724 track->to_units(oldposition, 0),
733 //printf("Edits::modify_handle 2 %s\n", track->title);
734 // move start of edit out
735 current_edit->shift_start_out(edit_mode,
736 track->to_units(newposition, 0),
737 track->to_units(oldposition, 0),
746 if(!result) current_edit = current_edit->next;
750 // right handle selected
751 for(current_edit = first; current_edit && !result;) {
752 if(edl->equivalent(track->from_units(current_edit->startproject) +
753 track->from_units(current_edit->length), oldposition)) {
754 oldposition = track->from_units(current_edit->startproject) +
755 track->from_units(current_edit->length);
758 //printf("Edits::modify_handle 3\n");
759 if(newposition <= oldposition) {
760 // shift end of edit in
761 //printf("Edits::modify_handle 4\n");
762 current_edit->shift_end_in(edit_mode,
763 track->to_units(newposition, 0),
764 track->to_units(oldposition, 0),
770 //printf("Edits::modify_handle 5\n");
774 // move end of edit out
775 //printf("Edits::modify_handle %d edit_mode=%d\n", __LINE__, edit_mode);
776 current_edit->shift_end_out(edit_mode,
777 track->to_units(newposition, 0),
778 track->to_units(oldposition, 0),
784 //printf("Edits::modify_handle 7\n");
788 if(!result) current_edit = current_edit->next;
789 //printf("Edits::modify_handle 8\n");
798 void Edits::paste_silence(int64_t start, int64_t end)
800 Edit *new_edit = editof(start, PLAY_FORWARD, 0);
801 if (!new_edit) return;
803 if( !new_edit->silence() ) {
804 new_edit = insert_new_edit(start);
805 new_edit->length = end - start;
808 new_edit->length += end - start;
810 for(Edit *current = new_edit->next; current; current = NEXT) {
811 current->startproject += end - start;
817 Edit *Edits::create_silence(int64_t start, int64_t end)
819 Edit *new_edit = insert_new_edit(start);
820 new_edit->length = end - start;
821 for(Edit *current = new_edit->next; current; current = NEXT) {
822 current->startproject += end - start;
827 Edit* Edits::shift(int64_t position, int64_t difference)
829 Edit *new_edit = split_edit(position);
831 for(Edit *current = first; current; current = NEXT) {
832 if(current->startproject >= position) {
833 current->shift(difference);
840 void Edits::shift_keyframes_recursive(int64_t position, int64_t length)
842 track->shift_keyframes(position, length);
845 void Edits::shift_effects_recursive(int64_t position, int64_t length, int edit_autos)
847 track->shift_effects(position, length, edit_autos);