r870: Merge 2.1:
[cinelerra_cv.git] / cinelerra / pluginset.C
blob7cc8d75317b2ee9bf013958dbd700b71c5fd1576
1 #include "edl.h"
2 #include "edlsession.h"
3 #include "filexml.h"
4 #include "keyframe.h"
5 #include "keyframes.h"
6 #include "plugin.h"
7 #include "pluginautos.h"
8 #include "pluginset.h"
9 #include "track.h"
11 #include <string.h>
13 PluginSet::PluginSet(EDL *edl, Track *track)
14  : Edits(edl, track, create_edit())
16         record = 1;
19 PluginSet::~PluginSet()
21         while(last) delete last;
25 PluginSet& PluginSet::operator=(PluginSet& plugins)
27 printf("PluginSet::operator= 1\n");
28         copy_from(&plugins);
29         return *this;
32 void PluginSet::copy_from(PluginSet *src)
34         while(last) delete last;
35         for(Plugin *current = (Plugin*)src->first; current; current = (Plugin*)NEXT)
36         {
37                 Plugin *new_plugin;
38                 append(new_plugin = (Plugin*)create_edit());
39                 new_plugin->copy_from(current);
40         }
41         this->record = src->record;
44 Plugin* PluginSet::get_first_plugin()
46 // Called when a new pluginset is added.
47 // Get first non-silence plugin in the plugin set.
48         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
49         {
50                 if(current && current->plugin_type != PLUGIN_NONE)
51                 {
52                         return current;
53                 }
54         }
55         return 0;
58 int64_t PluginSet::plugin_change_duration(int64_t input_position, 
59         int64_t input_length, 
60         int reverse)
62         int result = input_length;
63         Edit *current;
65         if(reverse)
66         {
67                 int input_start = input_position - input_length;
68                 for(current = last; current; current = PREVIOUS)
69                 {
70                         int start = current->startproject;
71                         int end = start + current->length;
72                         if(end > input_start && end < input_position)
73                         {
74                                 result = input_position - end;
75                                 return result;
76                         }
77                         else
78                         if(start > input_start && start < input_position)
79                         {
80                                 result = input_position - start;
81                                 return result;
82                         }
83                 }
84         }
85         else
86         {
87                 int input_end = input_position + input_length;
88                 for(current = first; current; current = NEXT)
89                 {
90                         int start = current->startproject;
91                         int end = start + current->length;
92                         if(start > input_position && start < input_end)
93                         {
94                                 result = start - input_position;
95                                 return result;
96                         }
97                         else
98                         if(end > input_position && end < input_end)
99                         {
100                                 result = end - input_position;
101                                 return result;
102                         }
103                 }
104         }
105         return input_length;
108 void PluginSet::synchronize_params(PluginSet *plugin_set)
110         for(Plugin *this_plugin = (Plugin*)first, *that_plugin = (Plugin*)plugin_set->first;
111                 this_plugin && that_plugin;
112                 this_plugin = (Plugin*)this_plugin->next, that_plugin = (Plugin*)that_plugin->next)
113         {
114                 this_plugin->synchronize_params(that_plugin);
115         }
118 Plugin* PluginSet::insert_plugin(char *title, 
119         int64_t unit_position, 
120         int64_t unit_length,
121         int plugin_type,
122         SharedLocation *shared_location,
123         KeyFrame *default_keyframe,
124         int do_optimize)
126         Plugin *plugin = (Plugin*)create_and_insert_edit(unit_position, 
127                 unit_position + unit_length);
130         if(title) strcpy(plugin->title, title);
132         if(shared_location) plugin->shared_location = *shared_location;
134         plugin->plugin_type = plugin_type;
136         if(default_keyframe) 
137                 *plugin->keyframes->default_auto = *default_keyframe;
138         plugin->keyframes->default_auto->position = unit_position;
140 // May delete the plugin we just added so not desirable while loading.
141         if(do_optimize) optimize();
142         return plugin;
145 Edit* PluginSet::create_edit()
147         Plugin* result = new Plugin(edl, this, "");
148         return result;
151 Edit* PluginSet::insert_edit_after(Edit *previous_edit)
153         Plugin *current = new Plugin(edl, this, "");
154         List<Edit>::insert_after(previous_edit, current);
155         return (Edit*)current;
159 int PluginSet::get_number()
161         return track->plugin_set.number_of(this);
164 void PluginSet::clear(int64_t start, int64_t end)
166 // Clear keyframes
167         for(Plugin *current = (Plugin*)first;
168                 current;
169                 current = (Plugin*)NEXT)
170         {
171                 current->keyframes->clear(start, end, 1);
172         }
174 // Clear edits
175         Edits::clear(start, end);
178 void PluginSet::clear_recursive(int64_t start, int64_t end)
180 //printf("PluginSet::clear_recursive 1\n");
181         clear(start, end);
184 void PluginSet::shift_keyframes_recursive(int64_t position, int64_t length)
186 // Plugin keyframes are shifted in shift_effects
189 void PluginSet::shift_effects_recursive(int64_t position, int64_t length)
191 // Effects are shifted in length extension
192 //      shift_effects(position, length);
196 void PluginSet::clear_keyframes(int64_t start, int64_t end)
198         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
199         {
200                 current->clear_keyframes(start, end);
201         }
204 void PluginSet::copy_keyframes(int64_t start, 
205         int64_t end, 
206         FileXML *file, 
207         int default_only,
208         int autos_only)
210         file->tag.set_title("PLUGINSET");       
211         file->append_tag();
212         file->append_newline();
214         for(Plugin *current = (Plugin*)first; 
215                 current; 
216                 current = (Plugin*)NEXT)
217         {
218                 current->copy_keyframes(start, end, file, default_only, autos_only);
219         }
221         file->tag.set_title("/PLUGINSET");      
222         file->append_tag();
223         file->append_newline();
227 void PluginSet::paste_keyframes(int64_t start, 
228         int64_t length, 
229         FileXML *file, 
230         int default_only,
231         Track *track)
233         int result = 0;
234         Plugin *current;
235         
236         PluginSet *target_pluginset;
237         Plugin *first_target_plugin = 0;
239         ArrayList<PluginSet*> unused_pluginsets;
240         
241 // get all available target pluginsets, we will be removing them one by one when we will paste into them
242         for (int i = 0; i < track->plugin_set.total; i++)
243         {
244                 unused_pluginsets.append(track->plugin_set.values[i]);
245         }
246         
247         char data[MESSAGESIZE];
248         char data_default_keyframe[MESSAGESIZE];
249         int default_keyframe;
250         int do_default_keyframe = 0;
251         
252         while(!result)
253         {
254                 result = file->read_tag();
256                 if(!result)
257                 {
258                         if(file->tag.title_is("/PLUGINSETS"))
259                                 result = 1;
260                         else
261                         if(file->tag.title_is("PLUGINSET"))
262                         {
263                                 target_pluginset = 0;
264                                 first_target_plugin = 0;
265                         }
266                         else
267                         if(file->tag.title_is("KEYFRAME"))
268                         {
269                                 int64_t position = file->tag.get_property("POSITION", 0);
270                                 position += start;
271                                 if(file->tag.get_property("DEFAULT", 0))
272                                 {
273 // remember the default keyframe, we'll use it later
274                                         default_keyframe = 1; 
275                                         do_default_keyframe = 1;
276                                         file->read_text_until("/KEYFRAME", data_default_keyframe, MESSAGESIZE);
277                                 }
278                                 else
279                                 {
280                                         default_keyframe = 0;
281                                         file->read_text_until("/KEYFRAME", data, MESSAGESIZE);                          
282                                 
283                                 }
285 //                              printf("d: a%sb\n", data);
286                                 Plugin *picked_first_target = 0;
287                                 if (!target_pluginset && default_keyframe && default_only && strlen(data_default_keyframe) > 0)
288                                 {
289                                         strcpy(data, data_default_keyframe);
290                                 } 
291                                 if ((!target_pluginset && !default_keyframe && strlen(data) > 0) ||     
292                                     (!target_pluginset && default_keyframe && default_only && strlen(data_default_keyframe) > 0))        
293                                 {
294 // now try to find the target           
295                                         int name_len = strchr(data, ' ') - data + 1;
297                                         PluginSet *second_choice = 0;
298                                         Plugin *second_choice_first_target_plugin = 0;
299                                         for (int i = 0; i < unused_pluginsets.total && !target_pluginset; i++)
300                                         {
301                                                 PluginSet *pluginset = unused_pluginsets.values[i];
302                                                 Plugin *current;
303                                                 for(current = (Plugin*)(pluginset->last); 
304                                                         current;
305                                                         current = (Plugin*)PREVIOUS)
306                                                 {
307                                                         if(position >= current->startproject 
308                                                         && position <= current->length + current->startproject 
309                                                         && !strncmp(((KeyFrame *)current->keyframes->default_auto)->data, data, name_len))
310                                                         {
311                                                                 target_pluginset = pluginset;
312                                                                 first_target_plugin = current;
313                                                                 break;
314                                                         }
315                                                         if(position >= current->startproject 
316                                                         && !strncmp(((KeyFrame *)current->keyframes->default_auto)->data, data, name_len))
317                                                         {
318                                                                 second_choice = pluginset;
319                                                                 second_choice_first_target_plugin = current;
320                                                                 break;
321                                                         }
322                                                                                                 
323                                                 }
324                                         }
325                                         if (!target_pluginset) 
326                                         {
327                                                 target_pluginset = second_choice;
328                                                 first_target_plugin = second_choice_first_target_plugin;
329                                         }
330                                 }
331 //                              printf(" Target: %p\n", target_pluginset);
332                                 if (target_pluginset) 
333                                 {
334                                         unused_pluginsets.remove(target_pluginset);
335                                         if (do_default_keyframe)
336                                         {
337 // default plugin is always delayed
338                                                 KeyFrame *keyframe = (KeyFrame*)first_target_plugin->keyframes->default_auto;
339                                                 strcpy(keyframe->data, data_default_keyframe);
340                                                 keyframe->position = position;
341                                                 do_default_keyframe = 0;
342                                         }
343                                         if (!default_only && !default_keyframe)
344                                         {
345                                                 for(current = (Plugin*)target_pluginset->last; 
346                                                         current;
347                                                         current = (Plugin*)PREVIOUS)
348                                                 {
349                                                         if(position >= current->startproject)
350                                                         {
351                                                                 KeyFrame *keyframe;
352                                                                 keyframe = (KeyFrame*)current->keyframes->insert_auto(position);
353                                                                 strcpy(keyframe->data, data);
354                                                                 keyframe->position = position;
355                                                                 break;
356                                                         }
357                                                 }
358                                         }
359                                 }
360                                 
361 // Get plugin owning keyframe
363                                 for(current = (Plugin*)last; 
364                                         current;
365                                         current = (Plugin*)PREVIOUS)
366                                 {
367 // We want keyframes to exist beyond the end of the last plugin to
368 // make editing intuitive, but it will always be possible to 
369 // paste keyframes from one plugin into an incompatible plugin.
370                                         if(position >= current->startproject)
371                                         {
372                                                 KeyFrame *keyframe;
373                                                 if(file->tag.get_property("DEFAULT", 0) || default_only)
374                                                 {
375                                                         keyframe = (KeyFrame*)current->keyframes->default_auto;
376                                                 }
377                                                 else
378                                                 {
379                                                         keyframe = 
380                                                                 (KeyFrame*)current->keyframes->insert_auto(position);
381                                                 }
382                                                 keyframe->load(file);
383                                                 keyframe->position = position;
384                                                 break;
385                                         }
386                                 }
388                         }
389                 }
390         }
393 void PluginSet::shift_effects(int64_t start, int64_t length)
395         for(Plugin *current = (Plugin*)first;
396                 current;
397                 current = (Plugin*)NEXT)
398         {
399 // Shift beginning of this effect
400                 if(current->startproject >= start)
401                 {
402                         current->startproject += length;
403                 }
404                 else
405 // Extend end of this effect.
406 // In loading new files, the effect should extend to fill the entire track.
407 // In muting, the effect must extend to fill the gap if another effect follows.
408 // The user should use Settings->edit effects to disable this.
409                 if(current->startproject + current->length >= start)
410                 {
411                         current->length += length;
412                 }
414 // Shift keyframes in this effect.
415 // If the default keyframe lands on the starting point, it must be shifted
416 // since the effect start is shifted.
417                 if(current->keyframes->default_auto->position >= start)
418                         current->keyframes->default_auto->position += length;
420                 current->keyframes->paste_silence(start, start + length);
421         }
424 void PluginSet::copy(int64_t start, int64_t end, FileXML *file)
426         file->tag.set_title("PLUGINSET");       
427         file->tag.set_property("RECORD", record);
428         file->append_tag();
429         file->append_newline();
431         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
432         {
433                 current->copy(start, end, file);
434         }
436         file->tag.set_title("/PLUGINSET");      
437         file->append_tag();
438         file->append_newline();
441 void PluginSet::save(FileXML *file)
443         copy(0, length(), file);
446 void PluginSet::load(FileXML *file, uint32_t load_flags)
448         int result = 0;
449 // Current plugin being amended
450         Plugin *plugin = (Plugin*)first;
451         int64_t startproject = 0;
453         record = file->tag.get_property("RECORD", record);
454         do{
455                 result = file->read_tag();
458                 if(!result)
459                 {
460                         if(file->tag.title_is("/PLUGINSET"))
461                         {
462                                 result = 1;
463                         }
464                         else
465                         if(file->tag.title_is("PLUGIN"))
466                         {
467                                 int64_t length = file->tag.get_property("LENGTH", (int64_t)0);
468                                 loaded_length += length;
469                                 int plugin_type = file->tag.get_property("TYPE", 1);
470                                 char title[BCTEXTLEN];
471                                 title[0] = 0;
472                                 file->tag.get_property("TITLE", title);
473                                 SharedLocation shared_location;
474                                 shared_location.load(file);
477                                 if(load_flags & LOAD_EDITS)
478                                 {
479                                         plugin = insert_plugin(title, 
480                                                 startproject, 
481                                                 length,
482                                                 plugin_type,
483                                                 &shared_location,
484                                                 0,
485                                                 0);
486                                         plugin->load(file);
487                                         startproject += length;
488                                 }
489                                 else
490                                 if(load_flags & LOAD_AUTOMATION)
491                                 {
492                                         if(plugin)
493                                         {
494                                                 plugin->load(file);
495                                                 plugin = (Plugin*)plugin->next;
496                                         }
497                                 }
498                         }
499                 }
500         }while(!result);
501         optimize();
506 int PluginSet::optimize()
508         int result = 1;
509         Plugin *current_edit;
512 // Delete keyframes out of range
513         for(current_edit = (Plugin*)first;
514                 current_edit; 
515                 current_edit = (Plugin*)current_edit->next)
516         {
517                 current_edit->keyframes->default_auto->position = 0;
518                 for(KeyFrame *current_keyframe = (KeyFrame*)current_edit->keyframes->last;
519                         current_keyframe; )
520                 {
521                         KeyFrame *previous_keyframe = (KeyFrame*)current_keyframe->previous;
522                         if(current_keyframe->position > 
523                                 current_edit->startproject + current_edit->length ||
524                                 current_keyframe->position < current_edit->startproject)
525                         {
526                                 delete current_keyframe;
527                         }
528                         current_keyframe = previous_keyframe;
529                 }
530         }
532 // Insert silence between plugins
533         for(Plugin *current = (Plugin*)last; current; current = (Plugin*)PREVIOUS)
534         {
535                 if(current->previous)
536                 {
537                         Plugin *previous = (Plugin*)PREVIOUS;
539                         if(current->startproject - 
540                                 previous->startproject - 
541                                 previous->length > 0)
542                         {
543                                 Plugin *new_plugin = (Plugin*)create_edit();
544                                 insert_before(current, new_plugin);
545                                 new_plugin->startproject = previous->startproject + 
546                                         previous->length;
547                                 new_plugin->length = current->startproject - 
548                                         previous->startproject - 
549                                         previous->length;
550                         }
551                 }
552                 else
553                 if(current->startproject > 0)
554                 {
555                         Plugin *new_plugin = (Plugin*)create_edit();
556                         insert_before(current, new_plugin);
557                         new_plugin->length = current->startproject;
558                 }
559         }
562 // delete 0 length plugins
563         while(result)
564         {
565                 result = 0;
567                 for(current_edit = (Plugin*)first; 
568                         current_edit && !result; )
569                 {
570                         if(current_edit->length == 0)
571                         {
572                                 Plugin* next = (Plugin*)current_edit->next;
573                                 delete current_edit;
574                                 result = 1;
575                                 current_edit = next;
576                         }
577                         else
578                                 current_edit = (Plugin*)current_edit->next;
579                 }
582 // merge identical plugins with same keyframes
583                 for(current_edit = (Plugin*)first; 
584                         current_edit && current_edit->next && !result; )
585                 {
586                         Plugin *next_edit = (Plugin*)current_edit->next;
589 // plugins identical
590                         if(next_edit->identical(current_edit))
591                 {
592                         current_edit->length += next_edit->length;
593 // Merge keyframes
594                                 for(KeyFrame *source = (KeyFrame*)next_edit->keyframes->first;
595                                         source;
596                                         source = (KeyFrame*)source->next)
597                                 {
598                                         KeyFrame *dest = new KeyFrame(edl, current_edit->keyframes);
599                                         *dest = *source;
600                                         current_edit->keyframes->append(dest);
601                                 }
602                         remove(next_edit);
603                         result = 1;
604                 }
606                 current_edit = (Plugin*)current_edit->next;
607                 }
609         }
610         if (!last || !last->silence())
611         {
612 // No last empty edit available... create one
613                 Edit *empty_edit = create_edit();
614                 if (!last) 
615                         empty_edit->startproject = 0;
616                 else
617                         empty_edit->startproject = last->startproject + last->length;
618                 empty_edit->length = LAST_VIRTUAL_LENGTH;
619                 insert_after(last, empty_edit);
620         } else
621         {
622                 last->length = LAST_VIRTUAL_LENGTH;
623         }
626         return 0;
633 void PluginSet::dump()
635         printf("   PLUGIN_SET:\n");
636         for(Plugin *current = (Plugin*)first; current; current =  (Plugin*)NEXT)
637                 current->dump();