r726: Implementing ability to add textural info to the labels
[cinelerra_cv/mob.git] / cinelerra / edits.C
blob2d161cf117293dc0a2c076fc7b7c215b07ad3224
1 #include "aedit.h"
2 #include "asset.h"
3 #include "assets.h"
4 #include "automation.h"
5 #include "cache.h"
6 #include "clip.h"
7 #include "edit.h"
8 #include "edits.h"
9 #include "edl.h"
10 #include "edlsession.h"
11 #include "file.h"
12 #include "filexml.h"
13 #include "filesystem.h"
14 #include "localsession.h"
15 #include "plugin.h"
16 #include "strategies.inc"
17 #include "track.h"
18 #include "transition.h"
19 #include "transportque.inc"
21 #include <string.h>
23 Edits::Edits(EDL *edl, Track *track)
24  : List<Edit>()
26         this->edl = edl;
27         this->track = track;
30 Edits::~Edits()
35 void Edits::equivalent_output(Edits *edits, int64_t *result)
37 // For the case of plugin sets, a new plugin set may be created with
38 // plugins only starting after 0.  We only want to restart brender at
39 // the first plugin in this case.
40         for(Edit *current = first, *that_current = edits->first; 
41                 current || that_current; 
42                 current = NEXT,
43                 that_current = that_current->next)
44         {
45 //printf("Edits::equivalent_output 1 %d\n", *result);
46                 if(!current && that_current)
47                 {
48                         int64_t position1 = (last ? last->startproject + last->length : 0);
49                         int64_t position2 = that_current->startproject;
50                         if(*result < 0 || *result > MIN(position1, position2))
51                                 *result = MIN(position1, position2);
52                         break;
53                 }
54                 else
55                 if(current && !that_current)
56                 {
57                         int64_t position1 = (edits->last ? edits->last->startproject + edits->last->length : 0);
58                         int64_t position2 = current->startproject;
59                         if(*result < 0 || *result > MIN(position1, position2))
60                                 *result = MIN(position1, position2);
61                         break;
62                 }
63                 else
64                 {
65 //printf("Edits::equivalent_output 2 %d\n", *result);
66                         current->equivalent_output(that_current, result);
67 //printf("Edits::equivalent_output 3 %d\n", *result);
68                 }
69         }
72 void Edits::copy_from(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         }
83 Edits& Edits::operator=(Edits& edits)
85 printf("Edits::operator= 1\n");
86         copy_from(&edits);
87         return *this;
91 void Edits::insert_asset(Asset *asset,
92         int64_t length,
93         int64_t position,
94         int track_number)
96         Edit *new_edit = insert_new_edit(position);
98         new_edit->asset = asset;
99         new_edit->startsource = 0;
100         new_edit->startproject = position;
101         new_edit->length = length;
103         if(asset->audio_data)
104                 new_edit->channel = track_number % asset->channels;
105         else
106         if(asset->video_data)
107                 new_edit->channel = track_number % asset->layers;
109 //printf("Edits::insert_asset %d %d\n", new_edit->channel, new_edit->length);
110         for(Edit *current = new_edit->next; current; current = NEXT)
111         {
112                 current->startproject += length;
113         }
116 void Edits::insert_edits(Edits *source_edits, int64_t position)
118         int64_t clipboard_length = 
119                 track->to_units(source_edits->edl->local_session->clipboard_length, 1);
120         int64_t clipboard_end = position + clipboard_length;
123 // Fill region between end of edit table and beginning of pasted segment
124 // with silence.  Can't call from insert_new_edit because it's recursive.
125         if(position > length())
126         {
127                 paste_silence(length(), position);
128         }
131         for(Edit *source_edit = source_edits->first;
132                 source_edit;
133                 source_edit = source_edit->next)
134         {
135 // Update Assets
136                 Asset *dest_asset = edl->assets->update(source_edit->asset);
137 // Open destination area
138                 Edit *dest_edit = insert_new_edit(position + source_edit->startproject);
140                 dest_edit->copy_from(source_edit);
141                 dest_edit->asset = dest_asset;
142                 dest_edit->startproject = position + source_edit->startproject;
146 // Shift keyframes in source edit to their position in the
147 // destination edit for plugin case
148                 dest_edit->shift_keyframes(position);
152 // Shift following edits and keyframes in following edits by length
153 // in current source edit.
154                 for(Edit *future_edit = dest_edit->next;
155                         future_edit;
156                         future_edit = future_edit->next)
157                 {
158                         future_edit->startproject += dest_edit->length;
159                         future_edit->shift_keyframes(dest_edit->length);
160                 }
162 // Fill clipboard length with silence
163                 if(!source_edit->next && 
164                         dest_edit->startproject + dest_edit->length < clipboard_end)
165                 {
166                         paste_silence(dest_edit->startproject + dest_edit->length,
167                                 clipboard_end);
168                 }
169         }
173 // Native units
174 // Can't paste silence in here because it's used by paste_silence.
175 Edit* Edits::insert_new_edit(int64_t position)
177         Edit *current = 0;
178 //printf("Edits::insert_new_edit 1\n");
179         current = split_edit(position);
180         
181         // FIXME: This check can go out now... since split_edit always returns an edit!
182         if(current) current = PREVIOUS;
184 //printf("Edits::insert_new_edit 1\n");
185         Edit *new_edit = create_edit();
186 //printf("Edits::insert_new_edit 1\n");
187         insert_after(current, new_edit);
188         new_edit->startproject = position;
189 //printf("Edits::insert_new_edit 2\n");
190         return new_edit;
194 Edit* Edits::split_edit(int64_t position)
196 // Get edit containing position
197         Edit *edit = editof(position, PLAY_FORWARD, 0);
199 // No edit found
200         if(!edit)
201                 if (!last || last->startproject + last->length <= position)
202                 {
203                         // Even when track is completely empty or split is beyond last edit, return correct edit
204                         Edit *empty = create_edit();
205                         if (last)
206                                 empty->startproject = last->startproject + last->length; // end of last edit
207                         else
208                                 empty->startproject = 0; // empty track
209                         empty->length = position - empty->startproject;
210                         insert_after(last, empty);
211                         edit = empty;
212                 } else
213                 {  // now we are now surely in situation where we have a) broken edit list or b) negative position... report error!
214                         printf("ERROR!\n");       
215                         printf("Trying to insert edit at position, but failed: %lli\n", position);
216                         printf("Dump is here:\n");
217                         track->dump();
218                         return 0;
219                 }       
220 // Split would have created a 0 length
221 //      if(edit->startproject == position) return edit;
222 // Create anyway so the return value comes before position
224         Edit *new_edit = create_edit();
225         insert_after(edit, new_edit);
226         new_edit->copy_from(edit);
227         new_edit->length = new_edit->startproject + new_edit->length - position;
228         edit->length = position - edit->startproject;
229         new_edit->startproject = edit->startproject + edit->length;
230         new_edit->startsource += edit->length;
233 // Decide what to do with the transition
234         if(edit->length && edit->transition)
235         {
236                 delete new_edit->transition;
237                 new_edit->transition = 0;
238         }
240         if(edit->transition && edit->transition->length > edit->length) 
241                 edit->transition->length = edit->length;
242         if(new_edit->transition && new_edit->transition->length > new_edit->length)
243                 new_edit->transition->length = new_edit->length;
244         return new_edit;
247 int Edits::save(FileXML *xml, char *output_path)
249         copy(0, length(), xml, output_path);
250         return 0;
253 void Edits::resample(double old_rate, double new_rate)
255         for(Edit *current = first; current; current = NEXT)
256         {
257                 current->startproject = Units::to_int64((double)current->startproject / 
258                         old_rate * 
259                         new_rate);
260                 if(PREVIOUS) PREVIOUS->length = current->startproject - PREVIOUS->startproject;
261                 current->startsource = Units::to_int64((double)current->startsource /
262                         old_rate *
263                         new_rate);
264                 if(!NEXT) current->length = Units::to_int64((double)current->length /
265                         old_rate *
266                         new_rate);
267                 if(current->transition)
268                 {
269                         current->transition->length = Units::to_int64(
270                                 (double)current->transition->length /
271                                 old_rate *
272                                 new_rate);
273                 }
274                 current->resample(old_rate, new_rate);
275         }
291 int Edits::optimize()
293         int result = 1;
294         Edit *current;
296 //return 0;
297 // Sort edits by starting point
298         while(result)
299         {
300                 result = 0;
301                 
302                 for(current = first; current; current = NEXT)
303                 {
304                         Edit *next_edit = NEXT;
305                         
306                         if(next_edit && next_edit->startproject < current->startproject)
307                         {
308                                 swap(next_edit, current);
309                                 result = 1;
310                         }
311                 }
312         }
314 // Insert silence between edits which aren't consecutive
315         for(current = last; current; current = current->previous)
316         {
317                 if(current->previous)
318                 {
319                         Edit *previous_edit = current->previous;
320                         if(current->startproject - 
321                                 previous_edit->startproject -
322                                 previous_edit->length > 0)
323                         {
324                                 Edit *new_edit = create_edit();
325                                 insert_before(current, new_edit);
326                                 new_edit->startproject = previous_edit->startproject + previous_edit->length;
327                                 new_edit->length = current->startproject - 
328                                         previous_edit->startproject -
329                                         previous_edit->length;
330                         }
331                 }
332                 else
333                 if(current->startproject > 0)
334                 {
335                         Edit *new_edit = create_edit();
336                         insert_before(current, new_edit);
337                         new_edit->length = current->startproject;
338                 }
339         }
341         result = 1;
342         while(result)
343         {
344                 result = 0;
347 // delete 0 length edits
348                 for(current = first; 
349                         current && !result; )
350                 {
351                         if(current->length == 0)
352                         {
353                                 Edit* next = current->next;
354                                 delete current;
355                                 result = 1;
356                                 current = next;
357                         }
358                         else
359                                 current = current->next;
360                 }
362 // merge same files or transitions
363                 for(current = first; 
364                         current && current->next && !result; )
365                 {
366 // assets identical
367                         Edit *next_edit = current->next;
368                 if(current->asset == next_edit->asset && 
369                         (!current->asset ||
370                                         (current->startsource + current->length == next_edit->startsource &&
371                                 current->channel == next_edit->channel)
372                                 )
373                         )
374                 {       
375 // source positions are consecutive
376                         current->length += next_edit->length;
377                         remove(next_edit);
378                         result = 1;
379                 }
381                 current = (Plugin*)current->next;
382                 }
384 // delete last edit of 0 length or silence
385                 if(last && 
386                         (last->silence() || 
387                         !last->length))
388                 {
389                         delete last;
390                         result = 1;
391                 }
392         }
394 //track->dump();
395         return 0;
417 // ===================================== file operations
419 int Edits::load(FileXML *file, int track_offset)
421         int result = 0;
422         int64_t startproject = 0;
424         do{
425                 result = file->read_tag();
427 //printf("Edits::load 1 %s\n", file->tag.get_title());
428                 if(!result)
429                 {
430                         if(!strcmp(file->tag.get_title(), "EDIT"))
431                         {
432                                 load_edit(file, startproject, track_offset);
433                         }
434                         else
435                         if(!strcmp(file->tag.get_title(), "/EDITS"))
436                         {
437                                 result = 1;
438                         }
439                 }
440         }while(!result);
442 //track->dump();
443         optimize();
446 int Edits::load_edit(FileXML *file, int64_t &startproject, int track_offset)
448         Edit* current;
450 //printf("Edits::load_edit 1 %d\n", total());
451         current = append_new_edit();
452 //printf("Edits::load_edit 2 %d\n", total());
454         current->load_properties(file, startproject);
456         startproject += current->length;
458         int result = 0;
459 //printf("Edits::load_edit 1\n");
461         do{
462 //printf("Edits::load_edit 2\n");
463                 result = file->read_tag();
464 //printf("Edits::load_edit 3 %s\n", file->tag.get_title());
466                 if(!result)
467                 {
468                         if(file->tag.title_is("FILE"))
469                         {
470                                 char filename[1024];
471                                 sprintf(filename, SILENCE);
472                                 file->tag.get_property("SRC", filename);
473 // Extend path
474 //printf("Edits::load_edit 4 %s\n", filename);
475                                 if(strcmp(filename, SILENCE))
476                                 {
477                                         char directory[BCTEXTLEN], edl_directory[BCTEXTLEN];
478                                         FileSystem fs;
479                                         fs.set_current_dir("");
480                                         fs.extract_dir(directory, filename);
481                                         if(!strlen(directory))
482                                         {
483                                                 fs.extract_dir(edl_directory, file->filename);
484                                                 fs.join_names(directory, edl_directory, filename);
485                                                 strcpy(filename, directory);
486                                         }
487                                         current->asset = edl->assets->get_asset(filename);
488                                 }
489                                 else
490                                 {
491                                         current->asset = edl->assets->get_asset(SILENCE);
492                                 }
493 //printf("Edits::load_edit 5\n");
494                         }
495                         else
496                         if(file->tag.title_is("TRANSITION"))
497                         {
498                                 current->transition = new Transition(edl,
499                                         current, 
500                                         "",
501                                         track->to_units(edl->session->default_transition_length, 1));
502                                 current->transition->load_xml(file);
503                         }
504                         else
505                         if(file->tag.title_is(SILENCE))
506                         {
507 //printf("Edits::load_edit 6\n");
508                                 current->asset = edl->assets->get_asset(SILENCE);
509 //printf("Edits::load_edit 7\n");
510                         }
511                         else
512                         if(file->tag.title_is("/EDIT"))
513                         {
514                                 result = 1;
515                         }
516                 }
517 //printf("Edits::load_edit 8\n");
518         }while(!result);
520 //printf("Edits::load_edit 5\n");
521 // in case of incomplete edit tag
522         if(!current->asset) current->asset = edl->assets->get_asset(SILENCE);
523         return 0;
526 // ============================================= accounting
528 int64_t Edits::length()
530         if(last) 
531                 return last->startproject + last->length;
532         else 
533                 return 0;
535 // 
536 // int64_t Edits::total_length() 
537 // {
538 //      int64_t total = 0;
539 //      Edit* current;
540 //      for(current = first; current; current = NEXT)
541 //      {
542 //              total += current->length;
543 //      }
544 //      return total; 
545 // };
547 Edit* Edits::editof(int64_t position, int direction, int use_nudge)
549         Edit *current = 0;
550         if(use_nudge && track) position += track->nudge;
552         if(direction == PLAY_FORWARD)
553         {
554                 for(current = last; current; current = PREVIOUS)
555                 {
556                         if(current->startproject <= position && current->startproject + current->length > position)
557                                 return current;
558                 }
559         }
560         else
561         if(direction == PLAY_REVERSE)
562         {
563                 for(current = first; current; current = NEXT)
564                 {
565                         if(current->startproject < position && current->startproject + current->length >= position)
566                                 return current;
567                 }
568         }
570         return 0;     // return 0 on failure
573 Edit* Edits::get_playable_edit(int64_t position, int use_nudge)
575         Edit *current;
576         if(track && use_nudge) position += track->nudge;
578 // Get the current edit
579         for(current = first; current; current = NEXT)
580         {
581                 if(current->startproject <= position && 
582                         current->startproject + current->length > position)
583                         break;
584         }
586 // Get the edit's asset
587         if(current)
588         {
589                 if(!current->asset)
590                         current = 0;
591         }
593         return current;     // return 0 on failure
596 // ================================================ editing
600 int Edits::copy(int64_t start, int64_t end, FileXML *file, char *output_path)
602         Edit *current_edit;
604         file->tag.set_title("EDITS");
605         file->append_tag();
606         file->append_newline();
608         for(current_edit = first; current_edit; current_edit = current_edit->next)
609         {
610                 current_edit->copy(start, end, file, output_path);
611         }
613         file->tag.set_title("/EDITS");
614         file->append_tag();
615         file->append_newline();
620 void Edits::clear(int64_t start, int64_t end)
622         Edit* edit1 = editof(start, PLAY_FORWARD, 0);
623         Edit* edit2 = editof(end, PLAY_FORWARD, 0);
624         Edit* current_edit;
626         if(end == start) return;        // nothing selected
627         if(!edit1 && !edit2) return;       // nothing selected
630         if(!edit2)
631         {                // edit2 beyond end of track
632                 edit2 = last;
633                 end = this->length();
634         }
636         if(edit1 != edit2)
637         {
638 // in different edits
640 //printf("Edits::clear 3.5 %d %d %d %d\n", edit1->startproject, edit1->length, edit2->startproject, edit2->length);
641                 edit1->length = start - edit1->startproject;
642                 edit2->length -= end - edit2->startproject;
643                 edit2->startsource += end - edit2->startproject;
644                 edit2->startproject += end - edit2->startproject;
646 // delete
647                 for(current_edit = edit1->next; current_edit && current_edit != edit2;)
648                 {
649                         Edit* next = current_edit->next;
650                         remove(current_edit);
651                         current_edit = next;
652                 }
653 // shift
654                 for(current_edit = edit2; current_edit; current_edit = current_edit->next)
655                 {
656                         current_edit->startproject -= end - start;
657                 }
658         }
659         else
660         {
661 // in same edit. paste_edit depends on this
662 // create a new edit
663                 current_edit = split_edit(start);
665                 current_edit->length -= end - start;
666                 current_edit->startsource += end - start;
668 // shift
669                 for(current_edit = current_edit->next; 
670                         current_edit; 
671                         current_edit = current_edit->next)
672                 {            
673                         current_edit->startproject -= end - start;
674                 }
675         }
677         optimize();
680 // Used by edit handle and plugin handle movement but plugin handle movement
681 // can only effect other plugins.
682 void Edits::clear_recursive(int64_t start, 
683         int64_t end, 
684         int edit_edits,
685         int edit_labels, 
686         int edit_plugins,
687         Edits *trim_edits)
689 //printf("Edits::clear_recursive 1\n");
690         track->clear(start, 
691                 end, 
692                 edit_edits,
693                 edit_labels,
694                 edit_plugins,
695                 0,
696                 trim_edits);
700 int Edits::clear_handle(double start, 
701         double end, 
702         int edit_plugins, 
703         double &distance)
705         Edit *current_edit;
707         distance = 0.0; // if nothing is found, distance is 0!
708         for(current_edit = first; 
709                 current_edit && current_edit->next; 
710                 current_edit = current_edit->next)
711         {
715                 if(current_edit->asset && 
716                         current_edit->next->asset)
717                 {
719                         if(current_edit->asset->equivalent(*current_edit->next->asset,
720                                 0,
721                                 0))
722                         {
724 // Got two consecutive edits in same source
725                                 if(edl->equivalent(track->from_units(current_edit->next->startproject), 
726                                         start))
727                                 {
728 // handle selected
729                                         int length = -current_edit->length;
730                                         current_edit->length = current_edit->next->startsource - current_edit->startsource;
731                                         length += current_edit->length;
733 // Lengthen automation
734                                         track->automation->paste_silence(current_edit->next->startproject, 
735                                                 current_edit->next->startproject + length);
737 // Lengthen effects
738                                         if(edit_plugins)
739                                                 track->shift_effects(current_edit->next->startproject, 
740                                                         length,
741                                                         0);
743                                         for(current_edit = current_edit->next; current_edit; current_edit = current_edit->next)
744                                         {
745                                                 current_edit->startproject += length;
746                                         }
748                                         distance = track->from_units(length);
749                                         optimize();
750                                         break;
751                                 }
752                         }
753                 }
754         }
756         return 0;
759 int Edits::modify_handles(double oldposition, 
760         double newposition, 
761         int currentend,
762         int edit_mode, 
763         int edit_edits,
764         int edit_labels,
765         int edit_plugins,
766         Edits *trim_edits)
768         int result = 0;
769         Edit *current_edit;
771 //printf("Edits::modify_handles 1 %d %f %f\n", currentend, newposition, oldposition);
772         if(currentend == 0)
773         {
774 // left handle
775                 for(current_edit = first; current_edit && !result;)
776                 {
777                         if(edl->equivalent(track->from_units(current_edit->startproject), 
778                                 oldposition))
779                         {
780 // edit matches selection
781 //printf("Edits::modify_handles 3 %f %f\n", newposition, oldposition);
782                                 oldposition = track->from_units(current_edit->startproject);
783                                 result = 1;
785                                 if(newposition >= oldposition)
786                                 {
787 //printf("Edits::modify_handle 1 %s %f %f\n", track->title, oldposition, newposition);
788 // shift start of edit in
789                                         current_edit->shift_start_in(edit_mode, 
790                                                 track->to_units(newposition, 0), 
791                                                 track->to_units(oldposition, 0),
792                                                 edit_edits,
793                                                 edit_labels,
794                                                 edit_plugins,
795                                                 trim_edits);
796                                 }
797                                 else
798                                 {
799 //printf("Edits::modify_handle 2 %s\n", track->title);
800 // move start of edit out
801                                         current_edit->shift_start_out(edit_mode, 
802                                                 track->to_units(newposition, 0), 
803                                                 track->to_units(oldposition, 0),
804                                                 edit_edits,
805                                                 edit_labels,
806                                                 edit_plugins,
807                                                 trim_edits);
808                                 }
809                         }
811                         if(!result) current_edit = current_edit->next;
812                 }
813         }
814         else
815         {
816 // right handle selected
817                 for(current_edit = first; current_edit && !result;)
818                 {
819                         if(edl->equivalent(track->from_units(current_edit->startproject) + 
820                                 track->from_units(current_edit->length), oldposition))
821                         {
822                 oldposition = track->from_units(current_edit->startproject) + 
823                                         track->from_units(current_edit->length);
824                                 result = 1;
826 //printf("Edits::modify_handle 3\n");
827                                 if(newposition <= oldposition)
828                                 {     
829 // shift end of edit in
830 //printf("Edits::modify_handle 4\n");
831                                         current_edit->shift_end_in(edit_mode, 
832                                                 track->to_units(newposition, 0), 
833                                                 track->to_units(oldposition, 0),
834                                                 edit_edits,
835                                                 edit_labels,
836                                                 edit_plugins,
837                                                 trim_edits);
838 //printf("Edits::modify_handle 5\n");
839                                 }
840                                 else
841                                 {     
842 // move end of edit out
843 //printf("Edits::modify_handle 6\n");
844                                         current_edit->shift_end_out(edit_mode, 
845                                                 track->to_units(newposition, 0), 
846                                                 track->to_units(oldposition, 0),
847                                                 edit_edits,
848                                                 edit_labels,
849                                                 edit_plugins,
850                                                 trim_edits);
851 //printf("Edits::modify_handle 7\n");
852                                 }
853                         }
855                         if(!result) current_edit = current_edit->next;
856 //printf("Edits::modify_handle 8\n");
857                 }
858         }
860         optimize();
861         return 0;
865 // Paste silence should not return anything - since pasting silence to an empty track should produce no edits
866 // If we need rutine to create new edit by pushing others forward, write new rutine and call it properly
867 // This are two distinctive functions
868 // This rutine leaves edits in optimized state!
869 void Edits::paste_silence(int64_t start, int64_t end)
871         // paste silence does not do anything if 
872         // a) paste silence is on empty track
873         // b) paste silence is after last edit
874         // in both cases editof returns NULL
875         Edit *new_edit = editof(start, PLAY_FORWARD, 0);
876         if (!new_edit) return;
878         if (!new_edit->asset)
879         { // in this case we extend already existing edit
880                 new_edit->length += end - start;
881         } else
882         { // we are in fact creating a new edit
883                 new_edit = insert_new_edit(start);
884                 new_edit->length = end - start;
885         }
886         for(Edit *current = new_edit->next; current; current = NEXT)
887         {
888                 current->startproject += end - start;
889         }
890         return;
893 // Used by other editing commands so don't optimize
894 // This is separate from paste_silence, since it has to wrok also on empty tracks/beyond end of track
895 Edit *Edits::create_and_insert_edit(int64_t start, int64_t end)
897         Edit *new_edit = insert_new_edit(start);
898         new_edit->length = end - start;
899         for(Edit *current = new_edit->next; current; current = NEXT)
900         {
901                 current->startproject += end - start;
902         }
903         return new_edit;
905                                      
906 Edit* Edits::shift(int64_t position, int64_t difference)
908         Edit *new_edit = split_edit(position);
910         for(Edit *current = first; 
911                 current; 
912                 current = NEXT)
913         {
914                 if(current->startproject >= position)
915                 {
916                         current->shift(difference);
917                 }
918         }
919         return new_edit;
923 void Edits::shift_keyframes_recursive(int64_t position, int64_t length)
925         track->shift_keyframes(position, length, 0);
928 void Edits::shift_effects_recursive(int64_t position, int64_t length)
930         track->shift_effects(position, length, 0);