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