r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / cinelerra / edits.C
blob6368f4ace24892702d725f43d5725afceb66e80a
1 #include "aedit.h"
2 #include "assets.h"
3 #include "automation.h"
4 #include "cache.h"
5 #include "clip.h"
6 #include "edit.h"
7 #include "edits.h"
8 #include "edl.h"
9 #include "edlsession.h"
10 #include "file.h"
11 #include "filexml.h"
12 #include "filesystem.h"
13 #include "localsession.h"
14 #include "plugin.h"
15 #include "strategies.inc"
16 #include "track.h"
17 #include "transition.h"
18 #include "transportque.inc"
20 #include <string.h>
22 Edits::Edits(EDL *edl, Track *track)
23  : List<Edit>()
25         this->edl = edl;
26         this->track = track;
29 Edits::~Edits()
34 void Edits::equivalent_output(Edits *edits, int64_t *result)
36 // For the case of plugin sets, a new plugin set may be created with
37 // plugins only starting after 0.  We only want to restart brender at
38 // the first plugin in this case.
39         for(Edit *current = first, *that_current = edits->first; 
40                 current || that_current; 
41                 current = NEXT,
42                 that_current = that_current->next)
43         {
44 //printf("Edits::equivalent_output 1 %d\n", *result);
45                 if(!current && that_current)
46                 {
47                         int64_t position1 = (last ? last->startproject + last->length : 0);
48                         int64_t position2 = that_current->startproject;
49                         if(*result < 0 || *result > MIN(position1, position2))
50                                 *result = MIN(position1, position2);
51                         break;
52                 }
53                 else
54                 if(current && !that_current)
55                 {
56                         int64_t position1 = (edits->last ? edits->last->startproject + edits->last->length : 0);
57                         int64_t position2 = current->startproject;
58                         if(*result < 0 || *result > MIN(position1, position2))
59                                 *result = MIN(position1, position2);
60                         break;
61                 }
62                 else
63                 {
64 //printf("Edits::equivalent_output 2 %d\n", *result);
65                         current->equivalent_output(that_current, result);
66 //printf("Edits::equivalent_output 3 %d\n", *result);
67                 }
68         }
72 Edits& Edits::operator=(Edits& edits)
74         while(last) delete last;
75         for(Edit *current = edits.first; current; current = NEXT)
76         {
77                 Edit *new_edit = append(create_edit());
78                 new_edit->copy_from(current);
79         }
80         return *this;
84 void Edits::insert_asset(Asset *asset,
85         int64_t length,
86         int64_t position,
87         int track_number)
89         Edit *new_edit = insert_new_edit(position);
91         new_edit->asset = asset;
92         new_edit->startsource = 0;
93         new_edit->startproject = position;
94         new_edit->length = length;
96         if(asset->audio_data)
97                 new_edit->channel = track_number % asset->channels;
98         else
99         if(asset->video_data)
100                 new_edit->channel = track_number % asset->layers;
102 //printf("Edits::insert_asset %d %d\n", new_edit->channel, new_edit->length);
103         for(Edit *current = new_edit->next; current; current = NEXT)
104         {
105                 current->startproject += length;
106         }
109 void Edits::insert_edits(Edits *source_edits, int64_t position)
111         int64_t clipboard_length = 
112                 track->to_units(source_edits->edl->local_session->clipboard_length, 1);
113         int64_t clipboard_end = position + clipboard_length;
116 // Fill region between end of edit table and beginning of pasted segment
117 // with silence.  Can't call from insert_new_edit because it's recursive.
118         if(position > length())
119         {
120                 paste_silence(length(), position);
121         }
124         for(Edit *source_edit = source_edits->first;
125                 source_edit;
126                 source_edit = source_edit->next)
127         {
128 // Update Assets
129                 Asset *dest_asset = edl->assets->update(source_edit->asset);
130 // Open destination area
131                 Edit *dest_edit = insert_new_edit(position + source_edit->startproject);
133                 dest_edit->copy_from(source_edit);
134                 dest_edit->asset = dest_asset;
135                 dest_edit->startproject = position + source_edit->startproject;
139 // Shift keyframes in source edit to their position in the
140 // destination edit for plugin case
141                 dest_edit->shift_keyframes(position);
145 // Shift following edits and keyframes in following edits by length
146 // in current source edit.
147                 for(Edit *future_edit = dest_edit->next;
148                         future_edit;
149                         future_edit = future_edit->next)
150                 {
151                         future_edit->startproject += dest_edit->length;
152                         future_edit->shift_keyframes(dest_edit->length);
153                 }
155 // Fill clipboard length with silence
156                 if(!source_edit->next && 
157                         dest_edit->startproject + dest_edit->length < clipboard_end)
158                 {
159                         paste_silence(dest_edit->startproject + dest_edit->length,
160                                 clipboard_end);
161                 }
162         }
166 // Native units
167 // Can't paste silence in here because it's used by paste_silence.
168 Edit* Edits::insert_new_edit(int64_t position)
170         Edit *current = 0;
171 //printf("Edits::insert_new_edit 1\n");
172         current = split_edit(position);
173         if(current) current = PREVIOUS;
175 //printf("Edits::insert_new_edit 1\n");
176         Edit *new_edit = create_edit();
177 //printf("Edits::insert_new_edit 1\n");
178         insert_after(current, new_edit);
179         new_edit->startproject = position;
180 //printf("Edits::insert_new_edit 2\n");
181         return new_edit;
185 Edit* Edits::split_edit(int64_t position)
187 // Get edit containing position
188         Edit *edit = editof(position, PLAY_FORWARD);
190 // No edit found
191         if(!edit)
192         {
193                 return 0;
194         }
195 // Split would have created a 0 length
196 //      if(edit->startproject == position) return edit;
197 // Create anyway so the return value comes before position
199         Edit *new_edit = create_edit();
200         insert_after(edit, new_edit);
201         new_edit->copy_from(edit);
202         new_edit->length = new_edit->startproject + new_edit->length - position;
203         edit->length = position - edit->startproject;
204         new_edit->startproject = edit->startproject + edit->length;
205         new_edit->startsource += edit->length;
208 // Decide what to do with the transition
209         if(edit->length && edit->transition)
210         {
211                 delete new_edit->transition;
212                 new_edit->transition = 0;
213         }
215         if(edit->transition && edit->transition->length > edit->length) 
216                 edit->transition->length = edit->length;
217         if(new_edit->transition && new_edit->transition->length > new_edit->length)
218                 new_edit->transition->length = new_edit->length;
219         return new_edit;
222 int Edits::save(FileXML *xml, char *output_path)
224         copy(0, length(), xml, output_path);
225         return 0;
228 void Edits::resample(double old_rate, double new_rate)
230         for(Edit *current = first; current; current = NEXT)
231         {
232                 current->startproject = Units::to_int64((double)current->startproject / 
233                         old_rate * 
234                         new_rate);
235                 if(PREVIOUS) PREVIOUS->length = current->startproject - PREVIOUS->startproject;
236                 current->startsource = Units::to_int64((double)current->startsource /
237                         old_rate *
238                         new_rate);
239                 if(!NEXT) current->length = Units::to_int64((double)current->length /
240                         old_rate *
241                         new_rate);
242                 current->resample(old_rate, new_rate);
243         }
259 int Edits::optimize()
261         int result = 1;
262         Edit *current;
264 //return 0;
265 // Sort edits by starting point
266         while(result)
267         {
268                 result = 0;
269                 
270                 for(current = first; current; current = NEXT)
271                 {
272                         Edit *next_edit = NEXT;
273                         
274                         if(next_edit && next_edit->startproject < current->startproject)
275                         {
276                                 swap(next_edit, current);
277                                 result = 1;
278                         }
279                 }
280         }
282 // Insert silence between edits which aren't consecutive
283         for(current = last; current; current = current->previous)
284         {
285                 if(current->previous)
286                 {
287                         Edit *previous_edit = current->previous;
288                         if(current->startproject - 
289                                 previous_edit->startproject -
290                                 previous_edit->length > 0)
291                         {
292                                 Edit *new_edit = create_edit();
293                                 insert_before(current, new_edit);
294                                 new_edit->startproject = previous_edit->startproject + previous_edit->length;
295                                 new_edit->length = current->startproject - 
296                                         previous_edit->startproject -
297                                         previous_edit->length;
298                         }
299                 }
300                 else
301                 if(current->startproject > 0)
302                 {
303                         Edit *new_edit = create_edit();
304                         insert_before(current, new_edit);
305                         new_edit->length = current->startproject;
306                 }
307         }
309         result = 1;
310         while(result)
311         {
312                 result = 0;
315 // delete 0 length edits
316                 for(current = first; 
317                         current && !result; )
318                 {
319                         if(current->length == 0)
320                         {
321                                 Edit* next = current->next;
322                                 delete current;
323                                 result = 1;
324                                 current = next;
325                         }
326                         else
327                                 current = current->next;
328                 }
330 // merge same files or transitions
331                 for(current = first; 
332                         current && current->next && !result; )
333                 {
334 // assets identical
335                         Edit *next_edit = current->next;
336                 if(current->asset == next_edit->asset && 
337                         (!current->asset ||
338                                         (current->startsource + current->length == next_edit->startsource &&
339                                 current->channel == next_edit->channel)
340                                 )
341                         )
342                 {       
343 // source positions are consecutive
344                         current->length += next_edit->length;
345                         remove(next_edit);
346                         result = 1;
347                 }
349                 current = (Plugin*)current->next;
350                 }
352 // delete last edit of 0 length or silence
353                 if(last && 
354                         (last->silence() || 
355                         !last->length))
356                 {
357                         delete last;
358                         result = 1;
359                 }
360         }
362 //track->dump();
363         return 0;
385 // ===================================== file operations
387 int Edits::load(FileXML *file, int track_offset)
389         int result = 0;
390         int64_t startproject = 0;
392         do{
393                 result = file->read_tag();
395 //printf("Edits::load 1 %s\n", file->tag.get_title());
396                 if(!result)
397                 {
398                         if(!strcmp(file->tag.get_title(), "EDIT"))
399                         {
400                                 load_edit(file, startproject, track_offset);
401                         }
402                         else
403                         if(!strcmp(file->tag.get_title(), "/EDITS"))
404                         {
405                                 result = 1;
406                         }
407                 }
408         }while(!result);
410 //track->dump();
411         optimize();
414 int Edits::load_edit(FileXML *file, int64_t &startproject, int track_offset)
416         Edit* current;
418 //printf("Edits::load_edit 1 %d\n", total());
419         current = append_new_edit();
420 //printf("Edits::load_edit 2 %d\n", total());
422         current->load_properties(file, startproject);
424         startproject += current->length;
426         int result = 0;
427 //printf("Edits::load_edit 1\n");
429         do{
430 //printf("Edits::load_edit 2\n");
431                 result = file->read_tag();
432 //printf("Edits::load_edit 3 %s\n", file->tag.get_title());
434                 if(!result)
435                 {
436                         if(file->tag.title_is("FILE"))
437                         {
438                                 char filename[1024];
439                                 sprintf(filename, SILENCE);
440                                 file->tag.get_property("SRC", filename);
441 // Extend path
442 //printf("Edits::load_edit 4 %s\n", filename);
443                                 if(strcmp(filename, SILENCE))
444                                 {
445                                         char directory[BCTEXTLEN], edl_directory[BCTEXTLEN];
446                                         FileSystem fs;
447                                         fs.set_current_dir("");
448                                         fs.extract_dir(directory, filename);
449                                         if(!strlen(directory))
450                                         {
451                                                 fs.extract_dir(edl_directory, file->filename);
452                                                 fs.join_names(directory, edl_directory, filename);
453                                                 strcpy(filename, directory);
454                                         }
455                                         current->asset = edl->assets->get_asset(filename);
456                                 }
457                                 else
458                                 {
459                                         current->asset = edl->assets->get_asset(SILENCE);
460                                 }
461 //printf("Edits::load_edit 5\n");
462                         }
463                         else
464                         if(file->tag.title_is("TRANSITION"))
465                         {
466                                 current->transition = new Transition(edl,
467                                         current, 
468                                         "",
469                                         track->to_units(edl->session->default_transition_length, 1));
470                                 current->transition->load_xml(file);
471                         }
472                         else
473                         if(file->tag.title_is(SILENCE))
474                         {
475 //printf("Edits::load_edit 6\n");
476                                 current->asset = edl->assets->get_asset(SILENCE);
477 //printf("Edits::load_edit 7\n");
478                         }
479                         else
480                         if(file->tag.title_is("/EDIT"))
481                         {
482                                 result = 1;
483                         }
484                 }
485 //printf("Edits::load_edit 8\n");
486         }while(!result);
488 //printf("Edits::load_edit 5\n");
489 // in case of incomplete edit tag
490         if(!current->asset) current->asset = edl->assets->get_asset(SILENCE);
491         return 0;
494 // ============================================= accounting
496 int64_t Edits::length()
498         if(last) 
499                 return last->startproject + last->length;
500         else 
501                 return 0;
503 // 
504 // int64_t Edits::total_length() 
505 // {
506 //      int64_t total = 0;
507 //      Edit* current;
508 //      for(current = first; current; current = NEXT)
509 //      {
510 //              total += current->length;
511 //      }
512 //      return total; 
513 // };
515 Edit* Edits::editof(int64_t position, int direction)
517         Edit *current = 0;
518         
519         if(direction == PLAY_FORWARD)
520         {
521                 for(current = last; current; current = PREVIOUS)
522                 {
523                         if(current->startproject <= position && current->startproject + current->length > position)
524                                 return current;
525                 }
526         }
527         else
528         if(direction == PLAY_REVERSE)
529         {
530                 for(current = first; current; current = NEXT)
531                 {
532                         if(current->startproject < position && current->startproject + current->length >= position)
533                                 return current;
534                 }
535         }
537         return 0;     // return 0 on failure
540 Edit* Edits::get_playable_edit(int64_t position)
542         Edit *current;
544 // Get the current edit
545         for(current = first; current; current = NEXT)
546         {
547                 if(current->startproject <= position && 
548                         current->startproject + current->length > position)
549                         break;
550         }
552 // Get the edit's asset
553         if(current)
554         {
555                 if(!current->asset)
556                         current = 0;
557         }
559         return current;     // return 0 on failure
562 // ================================================ editing
566 int Edits::copy(int64_t start, int64_t end, FileXML *file, char *output_path)
568         Edit *current_edit;
570         file->tag.set_title("EDITS");
571         file->append_tag();
572         file->append_newline();
574         for(current_edit = first; current_edit; current_edit = current_edit->next)
575         {
576                 current_edit->copy(start, end, file, output_path);
577         }
579         file->tag.set_title("/EDITS");
580         file->append_tag();
581         file->append_newline();
586 void Edits::clear(int64_t start, int64_t end)
588         Edit* edit1 = editof(start, PLAY_FORWARD);
589         Edit* edit2 = editof(end, PLAY_FORWARD);
590         Edit* current_edit;
592         if(end == start) return;        // nothing selected
593         if(!edit1 && !edit2) return;       // nothing selected
596         if(!edit2)
597         {                // edit2 beyond end of track
598                 edit2 = last;
599                 end = this->length();
600         }
602         if(edit1 != edit2)
603         {
604 // in different edits
606 //printf("Edits::clear 3.5 %d %d %d %d\n", edit1->startproject, edit1->length, edit2->startproject, edit2->length);
607                 edit1->length = start - edit1->startproject;
608                 edit2->length -= end - edit2->startproject;
609                 edit2->startsource += end - edit2->startproject;
610                 edit2->startproject += end - edit2->startproject;
612 // delete
613                 for(current_edit = edit1->next; current_edit && current_edit != edit2;)
614                 {
615                         Edit* next = current_edit->next;
616                         remove(current_edit);
617                         current_edit = next;
618                 }
619 // shift
620                 for(current_edit = edit2; current_edit; current_edit = current_edit->next)
621                 {
622                         current_edit->startproject -= end - start;
623                 }
624         }
625         else
626         {
627 // in same edit. paste_edit depends on this
628 // create a new edit
629                 current_edit = split_edit(start);
631                 current_edit->length -= end - start;
632                 current_edit->startsource += end - start;
634 // shift
635                 for(current_edit = current_edit->next; 
636                         current_edit; 
637                         current_edit = current_edit->next)
638                 {            
639                         current_edit->startproject -= end - start;
640                 }
641         }
643         optimize();
646 // Used by edit handle and plugin handle movement but plugin handle movement
647 // can only effect other plugins.
648 void Edits::clear_recursive(int64_t start, 
649         int64_t end, 
650         int edit_edits,
651         int edit_labels, 
652         int edit_plugins)
654 //printf("Edits::clear_recursive 1\n");
655         track->clear(start, 
656                 end, 
657                 edit_edits,
658                 edit_labels,
659                 edit_plugins,
660                 0);
664 int Edits::clear_handle(double start, 
665         double end, 
666         int edit_plugins, 
667         double &distance)
669         Edit *current_edit;
672         for(current_edit = first; 
673                 current_edit && current_edit->next; 
674                 current_edit = current_edit->next)
675         {
679                 if(current_edit->asset && 
680                         current_edit->next->asset)
681                 {
683                         if(current_edit->asset->equivalent(*current_edit->next->asset,
684                                 0,
685                                 0))
686                         {
688 // Got two consecutive edits in same source
689                                 if(edl->equivalent(track->from_units(current_edit->next->startproject), 
690                                         start))
691                                 {
692 // handle selected
693                                         int length = -current_edit->length;
694                                         current_edit->length = current_edit->next->startsource - current_edit->startsource;
695                                         length += current_edit->length;
697 // Lengthen automation
698                                         track->automation->paste_silence(current_edit->next->startproject, 
699                                                 current_edit->next->startproject + length);
701 // Lengthen effects
702                                         if(edit_plugins)
703                                                 track->shift_effects(current_edit->next->startproject, 
704                                                         length,
705                                                         0);
707                                         for(current_edit = current_edit->next; current_edit; current_edit = current_edit->next)
708                                         {
709                                                 current_edit->startproject += length;
710                                         }
712                                         distance = track->from_units(length);
713                                         optimize();
714                                         break;
715                                 }
716                         }
717                 }
718         }
720         return 0;
723 int Edits::modify_handles(double oldposition, 
724         double newposition, 
725         int currentend,
726         int edit_mode, 
727         int edit_edits,
728         int edit_labels,
729         int edit_plugins)
731         int result = 0;
732         Edit *current_edit;
734 //printf("Edits::modify_handles 1 %d %f %f\n", currentend, newposition, oldposition);
735         if(currentend == 0)
736         {
737 // left handle
738                 for(current_edit = first; current_edit && !result;)
739                 {
740 //printf("Edits::modify_handles 2 %f %f\n", 
741 //track->from_units(current_edit->startproject), 
742 //oldposition);
743                         if(edl->equivalent(track->from_units(current_edit->startproject), 
744                                 oldposition))
745                         {
746 // edit matches selection
747 //printf("Edits::modify_handles 3 %f %f\n", newposition, oldposition);
748                                 oldposition = track->from_units(current_edit->startproject);
749                                 result = 1;
751                                 if(newposition >= oldposition)
752                                 {
753 //printf("Edits::modify_handle 1 %s %f %f\n", track->title, oldposition, newposition);
754 // shift start of edit in
755                                         current_edit->shift_start_in(edit_mode, 
756                                                 track->to_units(newposition, 0), 
757                                                 track->to_units(oldposition, 0),
758                                                 edit_edits,
759                                                 edit_labels,
760                                                 edit_plugins);
761                                 }
762                                 else
763                                 {
764 //printf("Edits::modify_handle 2 %s\n", track->title);
765 // move start of edit out
766                                         current_edit->shift_start_out(edit_mode, 
767                                                 track->to_units(newposition, 0), 
768                                                 track->to_units(oldposition, 0),
769                                                 edit_edits,
770                                                 edit_labels,
771                                                 edit_plugins);
772                                 }
773                         }
775                         if(!result) current_edit = current_edit->next;
776                 }
777         }
778         else
779         {
780 // right handle selected
781                 for(current_edit = first; current_edit && !result;)
782                 {
783 //printf("Edits::modify_handle 1 %s\n", track->title);
784                         if(edl->equivalent(track->from_units(current_edit->startproject) + 
785                                 track->from_units(current_edit->length), oldposition))
786                         {
787 //printf("Edits::modify_handle 2\n");
788                 oldposition = track->from_units(current_edit->startproject) + 
789                                         track->from_units(current_edit->length);
790                                 result = 1;
792 //printf("Edits::modify_handle 3\n");
793                                 if(newposition <= oldposition)
794                                 {     // shift end of edit in
795 //printf("Edits::modify_handle 4\n");
796                                         current_edit->shift_end_in(edit_mode, 
797                                                 track->to_units(newposition, 0), 
798                                                 track->to_units(oldposition, 0),
799                                                 edit_edits,
800                                                 edit_labels,
801                                                 edit_plugins);
802 //printf("Edits::modify_handle 5\n");
803                                 }
804                                 else
805                                 {     // move end of edit out
806 //printf("Edits::modify_handle 6\n");
807                                         current_edit->shift_end_out(edit_mode, 
808                                                 track->to_units(newposition, 0), 
809                                                 track->to_units(oldposition, 0),
810                                                 edit_edits,
811                                                 edit_labels,
812                                                 edit_plugins);
813 //printf("Edits::modify_handle 7\n");
814                                 }
815                         }
817                         if(!result) current_edit = current_edit->next;
818 //printf("Edits::modify_handle 8\n");
819                 }
820         }
822         optimize();
823         return 0;
827 // Used by other editing commands so don't optimize
828 Edit* Edits::paste_silence(int64_t start, int64_t end)
830         Edit *new_edit = insert_new_edit(start);
831         new_edit->length = end - start;
832         for(Edit *current = new_edit->next; current; current = NEXT)
833         {
834                 current->startproject += end - start;
835         }
836         return new_edit;
838                                      
839 Edit* Edits::shift(int64_t position, int64_t difference)
841         Edit *new_edit = split_edit(position);
843         for(Edit *current = first; 
844                 current; 
845                 current = NEXT)
846         {
847                 if(current->startproject >= position)
848                 {
849                         current->shift(difference);
850                 }
851         }
852         return new_edit;
856 void Edits::shift_keyframes_recursive(int64_t position, int64_t length)
858         track->shift_keyframes(position, length, 0);
861 void Edits::shift_effects_recursive(int64_t position, int64_t length)
863         track->shift_effects(position, length, 0);