3658922df6d816a62cc1486e396072d7900649fd
[goodguy/history.git] / cinelerra-5.1 / cinelerra / edit.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "assets.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "edlsession.h"
30 #include "filexml.h"
31 #include "filesystem.h"
32 #include "localsession.h"
33 #include "plugin.h"
34 #include "mainsession.h"
35 #include "nestededls.h"
36 #include "trackcanvas.h"
37 #include "tracks.h"
38 #include "transition.h"
39 #include <string.h>
40
41
42 Edit::Edit()
43 {
44         reset();
45 }
46
47 Edit::Edit(EDL *edl, Track *track)
48 {
49         reset();
50         this->edl = edl;
51         this->track = track;
52         if(track) this->edits = track->edits;
53         id = EDL::next_id();
54 }
55
56 Edit::Edit(EDL *edl, Edits *edits)
57 {
58         reset();
59         this->edl = edl;
60         this->edits = edits;
61         if(edits) this->track = edits->track;
62         id = EDL::next_id();
63 }
64
65 Edit::~Edit()
66 {
67 //printf("Edit::~Edit 1\n");
68         if(transition) delete transition;
69 //printf("Edit::~Edit 2\n");
70 }
71
72 void Edit::reset()
73 {
74         edl = 0;
75         track = 0;
76         edits = 0;
77         startsource = 0;
78         startproject = 0;
79         length = 0;
80         asset = 0;
81         transition = 0;
82         channel = 0;
83         user_title[0] = 0;
84         nested_edl = 0;
85         is_plugin = 0;
86 }
87
88 Indexable* Edit::get_source()
89 {
90         if(asset) return asset;
91         if(nested_edl) return nested_edl;
92         return 0;
93 }
94
95 int Edit::copy(int64_t start,
96         int64_t end,
97         FileXML *file,
98         const char *output_path)
99 {
100 // variables
101 //printf("Edit::copy 1\n");
102
103         int64_t endproject = startproject + length;
104         int result;
105
106         if((startproject >= start && startproject <= end) ||  // startproject in range
107                  (endproject <= end && endproject >= start) ||     // endproject in range
108                  (startproject <= start && endproject >= end))    // range in project
109         {
110 // edit is in range
111                 int64_t startproject_in_selection = startproject; // start of edit in selection in project
112                 int64_t startsource_in_selection = startsource; // start of source in selection in source
113                 //int64_t endsource_in_selection = startsource + length; // end of source in selection
114                 int64_t length_in_selection = length;             // length of edit in selection
115 //printf("Edit::copy 2\n");
116
117                 if(startproject < start)
118                 {         // start is after start of edit in project
119                         int64_t length_difference = start - startproject;
120
121                         startsource_in_selection += length_difference;
122                         startproject_in_selection += length_difference;
123                         length_in_selection -= length_difference;
124                 }
125 //printf("Edit::copy 3\n");
126
127                 if(endproject > end)
128                 {         // end is before end of edit in project
129                         length_in_selection = end - startproject_in_selection;
130                 }
131
132 //printf("Edit::copy 4\n");
133                 if(file)    // only if not counting
134                 {
135                         file->tag.set_title("EDIT");
136                         file->tag.set_property("STARTSOURCE", startsource_in_selection);
137                         file->tag.set_property("CHANNEL", (int64_t)channel);
138                         file->tag.set_property("LENGTH", length_in_selection);
139                         if(user_title[0]) file->tag.set_property("USER_TITLE", user_title);
140 //printf("Edit::copy 5\n");
141
142                         copy_properties_derived(file, length_in_selection);
143
144                         file->append_tag();
145 //                      file->append_newline();
146 //printf("Edit::copy 6\n");
147
148                         if(nested_edl)
149                         {
150                                 file->tag.set_title("NESTED_EDL");
151                                 file->tag.set_property("SRC", nested_edl->path);
152                                 file->append_tag();
153                                 file->tag.set_title("/NESTED_EDL");
154                                 file->append_tag();
155                                 file->append_newline();
156                         }
157
158                         if(asset)
159                         {
160 //printf("Edit::copy 6 %s\n", asset->path);
161                                 char stored_path[BCTEXTLEN];
162                                 char asset_directory[BCTEXTLEN];
163                                 char output_directory[BCTEXTLEN];
164                                 FileSystem fs;
165
166 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
167                                 fs.extract_dir(asset_directory, asset->path);
168 //printf("Edit::copy %d %s\n", __LINE__, asset->path);
169
170                                 if(output_path)
171                                         fs.extract_dir(output_directory, output_path);
172                                 else
173                                         output_directory[0] = 0;
174 //printf("Edit::copy %s, %s %s, %s\n", asset->path, asset_directory, output_path, output_directory);
175
176                                 if(output_path && !strcmp(asset_directory, output_directory))
177                                         fs.extract_name(stored_path, asset->path);
178                                 else
179                                         strcpy(stored_path, asset->path);
180
181                                 file->tag.set_title("FILE");
182                                 file->tag.set_property("SRC", stored_path);
183                                 file->append_tag();
184                                 file->tag.set_title("/FILE");
185                                 file->append_tag();
186                         }
187
188                         if(transition)
189                         {
190                                 transition->save_xml(file);
191                         }
192
193 //printf("Edit::copy 7\n");
194                         file->tag.set_title("/EDIT");
195                         file->append_tag();
196                         file->append_newline();
197 //printf("Edit::copy 8\n");
198                 }
199 //printf("Edit::copy 9\n");
200                 result = 1;
201         }
202         else
203         {
204                 result = 0;
205         }
206 //printf("Edit::copy 10\n");
207         return result;
208 }
209
210
211 int64_t Edit::get_source_end(int64_t default_)
212 {
213         return default_;
214 }
215
216 void Edit::insert_transition(char *title)
217 {
218 //printf("Edit::insert_transition this=%p title=%p title=%s\n", this, title, title);
219         delete transition;
220         transition = new Transition(edl, this, title,
221                 track->to_units(edl->session->default_transition_length, 1));
222 }
223
224 void Edit::detach_transition()
225 {
226         if(transition) delete transition;
227         transition = 0;
228 }
229
230 int Edit::silence()
231 {
232         return asset || nested_edl ? 0 : 1;
233 }
234
235
236 void Edit::copy_from(Edit *edit)
237 {
238         this->nested_edl = edl->nested_edls->get_copy(edit->nested_edl);
239         this->asset = edl->assets->update(edit->asset);
240         this->startsource = edit->startsource;
241         this->startproject = edit->startproject;
242         this->length = edit->length;
243         strcpy (this->user_title, edit->user_title);
244
245         if(edit->transition)
246         {
247                 if(!transition) transition = new Transition(edl,
248                         this,
249                         edit->transition->title,
250                         edit->transition->length);
251                 *this->transition = *edit->transition;
252         }
253         this->channel = edit->channel;
254 }
255
256 void Edit::equivalent_output(Edit *edit, int64_t *result)
257 {
258 // End of edit changed
259         if(startproject + length != edit->startproject + edit->length)
260         {
261                 int64_t new_length = MIN(startproject + length,
262                         edit->startproject + edit->length);
263                 if(*result < 0 || new_length < *result)
264                         *result = new_length;
265         }
266
267         if(
268 // Different nested EDLs
269                 (edit->nested_edl && !nested_edl) ||
270                 (!edit->nested_edl && nested_edl) ||
271 // Different assets
272                 (edit->asset == 0 && asset != 0) ||
273                 (edit->asset != 0 && asset == 0) ||
274 // different transitions
275                 (edit->transition == 0 && transition != 0) ||
276                 (edit->transition != 0 && transition == 0) ||
277 // Position changed
278                 (startproject != edit->startproject) ||
279                 (startsource != edit->startsource) ||
280 // Transition changed
281                 (transition && edit->transition &&
282                         !transition->identical(edit->transition)) ||
283 // Asset changed
284                 (asset && edit->asset &&
285                         !asset->equivalent(*edit->asset, 1, 1, edl)) ||
286 // Nested EDL changed
287                 (nested_edl && edit->nested_edl &&
288                         strcmp(nested_edl->path, edit->nested_edl->path))
289                 )
290         {
291 // Start of edit changed
292                 if(*result < 0 || startproject < *result) *result = startproject;
293         }
294 }
295
296
297 Edit& Edit::operator=(Edit& edit)
298 {
299 //printf("Edit::operator= called\n");
300         copy_from(&edit);
301         return *this;
302 }
303
304 void Edit::synchronize_params(Edit *edit)
305 {
306         copy_from(edit);
307 }
308
309
310 // Comparison for ResourcePixmap drawing
311 int Edit::identical(Edit &edit)
312 {
313         int result = (this->nested_edl == edit.nested_edl &&
314                 this->asset == edit.asset &&
315                 this->startsource == edit.startsource &&
316                 this->startproject == edit.startproject &&
317                 this->length == edit.length &&
318                 this->transition == edit.transition &&
319                 this->channel == edit.channel);
320         return result;
321 }
322
323 int Edit::operator==(Edit &edit)
324 {
325         return identical(edit);
326 }
327
328 double Edit::frames_per_picon()
329 {
330         return Units::round(picon_w()) / frame_w();
331 }
332
333 double Edit::frame_w()
334 {
335         return track->from_units(1) *
336                 edl->session->sample_rate /
337                 edl->local_session->zoom_sample;
338 }
339
340 double Edit::picon_w()
341 {
342         int w = 0, h = 0;
343         if(asset) {
344                 w = asset->width;
345                 h = asset->height;
346         }
347         else if(nested_edl) {
348                 w = nested_edl->session->output_w;
349                 h = nested_edl->session->output_h;
350         }
351         return w>0 && h>0 ? ((double)edl->local_session->zoom_track*w)/h : 0;
352 }
353
354 int Edit::picon_h()
355 {
356         return edl->local_session->zoom_track;
357 }
358
359
360 int Edit::dump(FILE *fp)
361 {
362         fprintf(fp,"     EDIT %p\n", this); fflush(fp);
363         fprintf(fp,"      nested_edl=%p %s asset=%p %s\n",
364                 nested_edl,
365                 nested_edl ? nested_edl->path : "",
366                 asset,
367                 asset ? asset->path : "");
368         fflush(fp);
369         fprintf(fp,"      channel %d\n", channel);
370         if(transition)
371         {
372                 fprintf(fp,"      TRANSITION %p\n", transition);
373                 transition->dump(fp);
374         }
375         fprintf(fp,"      startsource %jd startproject %jd length %jd\n",
376                 startsource, startproject, length); fflush(fp);
377         return 0;
378 }
379
380 int Edit::load_properties(FileXML *file, int64_t &startproject)
381 {
382         startsource = file->tag.get_property("STARTSOURCE", (int64_t)0);
383         length = file->tag.get_property("LENGTH", (int64_t)0);
384         user_title[0] = 0;
385         file->tag.get_property("USER_TITLE", user_title);
386         this->startproject = startproject;
387         load_properties_derived(file);
388         return 0;
389 }
390
391 void Edit::shift(int64_t difference)
392 {
393 //printf("Edit::shift 1 %p %jd %jd\n", this, startproject, difference);
394         startproject += difference;
395 //printf("Edit::shift 2 %jd %jd\n", startproject, difference);
396 }
397
398 int Edit::shift_start_in(int edit_mode,
399         int64_t newposition,
400         int64_t oldposition,
401         int edit_edits,
402         int edit_labels,
403         int edit_plugins,
404         int edit_autos,
405         Edits *trim_edits)
406 {
407         int64_t cut_length = newposition - oldposition;
408         int64_t end_previous_source, end_source;
409
410         if(edit_mode == MOVE_ALL_EDITS)
411         {
412                 if(cut_length < length)
413                 {        // clear partial
414                         edits->clear_recursive(oldposition,
415                                 newposition,
416                                 edit_edits,
417                                 edit_labels,
418                                 edit_plugins,
419                                 edit_autos,
420                                 trim_edits);
421                 }
422                 else
423                 {        // clear entire
424                         edits->clear_recursive(oldposition,
425                                 startproject + length,
426                                 edit_edits,
427                                 edit_labels,
428                                 edit_plugins,
429                                 edit_autos,
430                                 trim_edits);
431                 }
432         }
433         else
434         if(edit_mode == MOVE_ONE_EDIT)
435         {
436 // Paste silence and cut
437 //printf("Edit::shift_start_in 1\n");
438                 if(!previous)
439                 {
440                         Edit *new_edit = edits->create_edit();
441                         new_edit->startproject = this->startproject;
442                         new_edit->length = 0;
443                         edits->insert_before(this,
444                                 new_edit);
445                 }
446 //printf("Edit::shift_start_in 2 %p\n", previous);
447
448                 end_previous_source = previous->get_source_end(previous->startsource + previous->length + cut_length);
449                 if(end_previous_source > 0 &&
450                         previous->startsource + previous->length + cut_length > end_previous_source)
451                         cut_length = end_previous_source - previous->startsource - previous->length;
452
453                 if(cut_length < length)
454                 {               // Move in partial
455                         startproject += cut_length;
456                         startsource += cut_length;
457                         length -= cut_length;
458                         previous->length += cut_length;
459 //printf("Edit::shift_start_in 2\n");
460                 }
461                 else
462                 {               // Clear entire edit
463                         cut_length = length;
464                         previous->length += cut_length;
465                         for(Edit* current_edit = this; current_edit; current_edit = current_edit->next)
466                         {
467                                 current_edit->startproject += cut_length;
468                         }
469                         edits->clear_recursive(oldposition + cut_length,
470                                 startproject + cut_length,
471                                 edit_edits,
472                                 edit_labels,
473                                 edit_plugins,
474                                 edit_autos,
475                                 trim_edits);
476                 }
477 //printf("Edit::shift_start_in 3\n");
478         }
479         else
480         if(edit_mode == MOVE_NO_EDITS)
481         {
482                 end_source = get_source_end(startsource + length + cut_length);
483                 if(end_source > 0 && startsource + length + cut_length > end_source)
484                         cut_length = end_source - startsource - length;
485
486                 startsource += cut_length;
487         }
488         return 0;
489 }
490
491 int Edit::shift_start_out(int edit_mode,
492         int64_t newposition,
493         int64_t oldposition,
494         int edit_edits,
495         int edit_labels,
496         int edit_plugins,
497         int edit_autos,
498         Edits *trim_edits)
499 {
500         int64_t cut_length = oldposition - newposition;
501
502
503         if(asset || nested_edl)
504         {
505                 int64_t end_source = get_source_end(1);
506
507 //printf("Edit::shift_start_out 1 %jd %jd\n", startsource, cut_length);
508                 if(end_source > 0 && startsource < cut_length)
509                 {
510                         cut_length = startsource;
511                 }
512         }
513
514         if(edit_mode == MOVE_ALL_EDITS)
515         {
516 //printf("Edit::shift_start_out 10 %jd\n", cut_length);
517                 startsource -= cut_length;
518                 length += cut_length;
519
520                 if(edit_autos)
521                         edits->shift_keyframes_recursive(startproject,
522                                 cut_length);
523                 if(edit_plugins)
524                         edits->shift_effects_recursive(startproject,
525                                 cut_length,
526                                 edit_autos);
527
528                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
529                 {
530                         current_edit->startproject += cut_length;
531                 }
532         }
533         else
534         if(edit_mode == MOVE_ONE_EDIT)
535         {
536                 if(previous)
537                 {
538                         if(cut_length < previous->length)
539                         {   // Cut into previous edit
540                                 previous->length -= cut_length;
541                                 startproject -= cut_length;
542                                 startsource -= cut_length;
543                                 length += cut_length;
544 printf("Edit::shift_start_out 2\n");
545                         }
546                         else
547                         {   // Clear entire previous edit
548                                 cut_length = previous->length;
549                                 previous->length = 0;
550                                 length += cut_length;
551                                 startsource -= cut_length;
552                                 startproject -= cut_length;
553                         }
554                 }
555         }
556         else
557         if(edit_mode == MOVE_NO_EDITS)
558         {
559                 startsource -= cut_length;
560         }
561
562 // Fix infinite length files
563         if(startsource < 0) startsource = 0;
564         return 0;
565 }
566
567 int Edit::shift_end_in(int edit_mode,
568         int64_t newposition,
569         int64_t oldposition,
570         int edit_edits,
571         int edit_labels,
572         int edit_plugins,
573         int edit_autos,
574         Edits *trim_edits)
575 {
576         int64_t cut_length = oldposition - newposition;
577
578         if(edit_mode == MOVE_ALL_EDITS)
579         {
580 //printf("Edit::shift_end_in 1\n");
581                 if(newposition > startproject)
582                 {        // clear partial edit
583 //printf("Edit::shift_end_in %p %p\n", track->edits, edits);
584                         edits->clear_recursive(newposition,
585                                 oldposition,
586                                 edit_edits,
587                                 edit_labels,
588                                 edit_plugins,
589                                 edit_autos,
590                                 trim_edits);
591                 }
592                 else
593                 {        // clear entire edit
594                         edits->clear_recursive(startproject,
595                                 oldposition,
596                                 edit_edits,
597                                 edit_labels,
598                                 edit_plugins,
599                                 edit_autos,
600                                 trim_edits);
601                 }
602         }
603         else
604         if(edit_mode == MOVE_ONE_EDIT)
605         {
606                 if(next)
607                 {
608                         if(next->asset)
609                         {
610                                 int64_t end_source = next->get_source_end(1);
611
612                                 if(end_source > 0 && next->startsource - cut_length < 0)
613                                 {
614                                         cut_length = next->startsource;
615                                 }
616                         }
617
618                         if(cut_length < length)
619                         {
620                                 length -= cut_length;
621                                 next->startproject -= cut_length;
622                                 next->startsource -= cut_length;
623                                 next->length += cut_length;
624 //printf("Edit::shift_end_in 2 %d\n", cut_length);
625                         }
626                         else
627                         {
628                                 cut_length = length;
629                                 next->length += cut_length;
630                                 next->startsource -= cut_length;
631                                 next->startproject -= cut_length;
632                                 length -= cut_length;
633                         }
634                 }
635                 else
636                 {
637                         if(cut_length < length)
638                         {
639                                 length -= cut_length;
640                         }
641                         else
642                         {
643                                 cut_length = length;
644                                 edits->clear_recursive(startproject,
645                                         oldposition,
646                                         edit_edits,
647                                         edit_labels,
648                                         edit_plugins,
649                                         edit_autos,
650                                         trim_edits);
651                         }
652                 }
653         }
654         else
655 // Does nothing for plugins
656         if(edit_mode == MOVE_NO_EDITS)
657         {
658 //printf("Edit::shift_end_in 3\n");
659                 int64_t end_source = get_source_end(1);
660                 if(end_source > 0 && startsource < cut_length)
661                 {
662                         cut_length = startsource;
663                 }
664                 startsource -= cut_length;
665         }
666         return 0;
667 }
668
669 int Edit::shift_end_out(int edit_mode,
670         int64_t newposition,
671         int64_t oldposition,
672         int edit_edits,
673         int edit_labels,
674         int edit_plugins,
675         int edit_autos,
676         Edits *trim_edits)
677 {
678         int64_t cut_length = newposition - oldposition;
679         int64_t endsource = get_source_end(startsource + length + cut_length);
680
681 // check end of edit against end of source file
682         if(endsource > 0 && startsource + length + cut_length > endsource)
683                 cut_length = endsource - startsource - length;
684
685 //printf("Edit::shift_end_out 1 %jd %d %d %d\n", oldposition, newposition, this->length, cut_length);
686         if(edit_mode == MOVE_ALL_EDITS)
687         {
688 // Extend length
689                 this->length += cut_length;
690
691 // Effects are shifted in length extension
692                 if(edit_plugins)
693                         edits->shift_effects_recursive(oldposition /* startproject */,
694                                 cut_length,
695                                 edit_autos);
696                 if(edit_autos)
697                         edits->shift_keyframes_recursive(oldposition /* startproject */,
698                                 cut_length);
699
700                 for(Edit* current_edit = next; current_edit; current_edit = current_edit->next)
701                 {
702                         current_edit->startproject += cut_length;
703                 }
704         }
705         else
706         if(edit_mode == MOVE_ONE_EDIT)
707         {
708                 if(next)
709                 {
710                         if(cut_length < next->length)
711                         {
712                                 length += cut_length;
713                                 next->startproject += cut_length;
714                                 next->startsource += cut_length;
715                                 next->length -= cut_length;
716 //printf("Edit::shift_end_out 2 %d\n", cut_length);
717                         }
718                         else
719                         {
720                                 cut_length = next->length;
721                                 next->length = 0;
722                                 length += cut_length;
723                         }
724                 }
725                 else
726                 {
727                         length += cut_length;
728                 }
729         }
730         else
731         if(edit_mode == MOVE_NO_EDITS)
732         {
733                 startsource += cut_length;
734         }
735         return 0;
736 }
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766 int Edit::popup_transition(float view_start, float zoom_units, int cursor_x, int cursor_y)
767 {
768         int64_t left, right, left_unit, right_unit;
769         if(!transition) return 0;
770         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
771
772         if(cursor_x > left && cursor_x < right)
773         {
774 //              transition->popup_transition(cursor_x, cursor_y);
775                 return 1;
776         }
777         return 0;
778 }
779
780 int Edit::select_handle(float view_start, float zoom_units, int cursor_x, int cursor_y, int64_t &selection)
781 {
782         int64_t left, right, left_unit, right_unit;
783         get_handle_parameters(left, right, left_unit, right_unit, view_start, zoom_units);
784
785         int64_t pixel1, pixel2;
786         pixel1 = left;
787         pixel2 = pixel1 + 10;
788
789 // test left edit
790 // cursor_x is faked in acanvas
791         if(cursor_x >= pixel1 && cursor_x <= pixel2)
792         {
793                 selection = left_unit;
794                 return 1;     // left handle
795         }
796
797         //int64_t endproject = startproject + length;
798         pixel2 = right;
799         pixel1 = pixel2 - 10;
800
801 // test right edit
802         if(cursor_x >= pixel1 && cursor_x <= pixel2)
803         {
804                 selection = right_unit;
805                 return 2;     // right handle
806         }
807         return 0;
808 }
809