Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / pluginset.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 "edl.h"
23 #include "edlsession.h"
24 #include "filexml.h"
25 #include "keyframe.h"
26 #include "keyframes.h"
27 #include "plugin.h"
28 #include "pluginautos.h"
29 #include "pluginset.h"
30 #include "track.h"
31
32 #include <string.h>
33
34 PluginSet::PluginSet(EDL *edl, Track *track)
35  : Edits(edl, track)
36 {
37         record = 1;
38 }
39
40 PluginSet::~PluginSet()
41 {
42         while(last) delete last;
43 }
44
45
46 PluginSet& PluginSet::operator=(PluginSet& plugins)
47 {
48 printf("PluginSet::operator= 1\n");
49         copy_from(&plugins);
50         return *this;
51 }
52
53 void PluginSet::copy_from(PluginSet *src)
54 {
55         while(last) delete last;
56         for(Plugin *current = (Plugin*)src->first; current; current = (Plugin*)NEXT)
57         {
58                 Plugin *new_plugin;
59                 append(new_plugin = (Plugin*)create_edit());
60                 new_plugin->copy_from(current);
61         }
62         this->record = src->record;
63 }
64
65 Plugin* PluginSet::get_first_plugin()
66 {
67 // Called when a new pluginset is added.
68 // Get first non-silence plugin in the plugin set.
69         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
70         {
71                 if(current && current->plugin_type != PLUGIN_NONE)
72                 {
73                         return current;
74                 }
75         }
76         return 0;
77 }
78
79 int64_t PluginSet::plugin_change_duration(int64_t input_position,
80         int64_t input_length,
81         int reverse)
82 {
83         int result = input_length;
84         Edit *current;
85
86         if(reverse)
87         {
88                 int input_start = input_position - input_length;
89                 for(current = last; current; current = PREVIOUS)
90                 {
91                         int start = current->startproject;
92                         int end = start + current->length;
93                         if(end > input_start && end < input_position)
94                         {
95                                 result = input_position - end;
96                                 return result;
97                         }
98                         else
99                         if(start > input_start && start < input_position)
100                         {
101                                 result = input_position - start;
102                                 return result;
103                         }
104                 }
105         }
106         else
107         {
108                 int input_end = input_position + input_length;
109                 for(current = first; current; current = NEXT)
110                 {
111                         int start = current->startproject;
112                         int end = start + current->length;
113                         if(start > input_position && start < input_end)
114                         {
115                                 result = start - input_position;
116                                 return result;
117                         }
118                         else
119                         if(end > input_position && end < input_end)
120                         {
121                                 result = end - input_position;
122                                 return result;
123                         }
124                 }
125         }
126         return input_length;
127 }
128
129 void PluginSet::synchronize_params(PluginSet *plugin_set)
130 {
131         for(Plugin *this_plugin = (Plugin*)first, *that_plugin = (Plugin*)plugin_set->first;
132                 this_plugin && that_plugin;
133                 this_plugin = (Plugin*)this_plugin->next, that_plugin = (Plugin*)that_plugin->next)
134         {
135                 this_plugin->synchronize_params(that_plugin);
136         }
137 }
138
139 Plugin* PluginSet::insert_plugin(const char *title,
140         int64_t unit_position,
141         int64_t unit_length,
142         int plugin_type,
143         SharedLocation *shared_location,
144         KeyFrame *default_keyframe,
145         int do_optimize)
146 {
147         Plugin *plugin = (Plugin*)create_silence(unit_position, unit_position + unit_length);
148         if(title) strcpy(plugin->title, title);
149         if(shared_location) plugin->shared_location = *shared_location;
150         plugin->plugin_type = plugin_type;
151
152         if(default_keyframe)
153                 *plugin->keyframes->default_auto = *default_keyframe;
154         plugin->keyframes->default_auto->position = unit_position;
155
156 // May delete the plugin we just added so not desirable while loading.
157         if(do_optimize) optimize();
158         return plugin;
159 }
160
161 Edit* PluginSet::create_edit()
162 {
163         Plugin* result = new Plugin(edl, this, "");
164         return result;
165 }
166
167 Edit* PluginSet::insert_edit_after(Edit *previous_edit)
168 {
169         Plugin *current = new Plugin(edl, this, "");
170         List<Edit>::insert_after(previous_edit, current);
171         return (Edit*)current;
172 }
173
174
175 int PluginSet::get_number()
176 {
177         return track->plugin_set.number_of(this);
178 }
179
180 void PluginSet::clear(int64_t start, int64_t end, int edit_autos)
181 {
182         if(edit_autos)
183         {
184 // Clear keyframes
185                 for(Plugin *current = (Plugin*)first;
186                         current;
187                         current = (Plugin*)NEXT)
188                 {
189                         current->keyframes->clear(start, end, 1);
190                 }
191         }
192
193 // Clear edits
194         Edits::clear(start, end);
195 }
196
197 //void PluginSet::clear_recursive(int64_t start, int64_t end)
198 //{
199 //printf("PluginSet::clear_recursive 1\n");
200 //      clear(start, end, 1);
201 //}
202
203 void PluginSet::shift_keyframes_recursive(int64_t position, int64_t length)
204 {
205 // Plugin keyframes are shifted in shift_effects
206 }
207
208 void PluginSet::shift_effects_recursive(int64_t position, int64_t length, int edit_autos)
209 {
210 // Effects are shifted in length extension
211 }
212
213
214 void PluginSet::clear_keyframes(int64_t start, int64_t end)
215 {
216         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
217         {
218                 current->clear_keyframes(start, end);
219         }
220 }
221
222 void PluginSet::copy_keyframes(int64_t start,
223         int64_t end,
224         FileXML *file,
225         int default_only,
226         int active_only)
227 {
228         file->tag.set_title("PLUGINSET");
229         file->append_tag();
230         file->append_newline();
231
232         for(Plugin *current = (Plugin*)first;
233                 current;
234                 current = (Plugin*)NEXT)
235         {
236                 current->copy_keyframes(start, end, file, default_only, active_only);
237         }
238
239         file->tag.set_title("/PLUGINSET");
240         file->append_tag();
241         file->append_newline();
242 }
243
244
245 void PluginSet::paste_keyframes(int64_t start,
246         int64_t length,
247         FileXML *file,
248         int default_only,
249         int active_only)
250 {
251         int result = 0;
252         int first_keyframe = 1;
253         Plugin *current;
254
255
256         while(!result)
257         {
258                 result = file->read_tag();
259
260                 if(!result)
261                 {
262                         if(file->tag.title_is("/PLUGINSET"))
263                                 result = 1;
264                         else
265                         if(file->tag.title_is("KEYFRAME"))
266                         {
267                                 int64_t position = file->tag.get_property("POSITION", 0);
268                                 if(first_keyframe && default_only)
269                                 {
270                                         position = start;
271                                 }
272                                 else
273                                 {
274                                         position += start;
275                                 }
276
277 // Get plugin owning keyframe
278                                 for(current = (Plugin*)last;
279                                         current;
280                                         current = (Plugin*)PREVIOUS)
281                                 {
282 // We want keyframes to exist beyond the end of the last plugin to
283 // make editing intuitive, but it will always be possible to
284 // paste keyframes from one plugin into an incompatible plugin.
285                                         if(position >= current->startproject)
286                                         {
287                                                 KeyFrame *keyframe = 0;
288                                                 if(file->tag.get_property("DEFAULT", 0) || default_only)
289                                                 {
290                                                         keyframe = (KeyFrame*)current->keyframes->default_auto;
291                                                 }
292                                                 else
293                                                 if(!default_only)
294                                                 {
295                                                         keyframe =
296                                                                 (KeyFrame*)current->keyframes->insert_auto(position);
297                                                 }
298
299                                                 if(keyframe)
300                                                 {
301                                                         keyframe->load(file);
302                                                         keyframe->position = position;
303                                                 }
304                                                 break;
305                                         }
306                                 }
307
308                                 first_keyframe = 0;
309                         }
310                 }
311         }
312 }
313
314 void PluginSet::shift_effects(int64_t start, int64_t length, int edit_autos)
315 {
316         for(Plugin *current = (Plugin*)first;
317                 current;
318                 current = (Plugin*)NEXT)
319         {
320 // Shift beginning of this effect
321                 if(current->startproject >= start)
322                 {
323                         current->startproject += length;
324                 }
325                 else
326 // Extend end of this effect.
327 // In loading new files, the effect should extend to fill the entire track.
328 // In muting, the effect must extend to fill the gap if another effect follows.
329 // The user should use Settings->edit effects to disable this.
330                 if(current->startproject + current->length >= start)
331                 {
332                         current->length += length;
333                 }
334
335 // Shift keyframes in this effect.
336 // If the default keyframe lands on the starting point, it must be shifted
337 // since the effect start is shifted.
338                 if(edit_autos && current->keyframes->default_auto->position >= start)
339                         current->keyframes->default_auto->position += length;
340
341                 if(edit_autos) current->keyframes->paste_silence(start, start + length);
342         }
343 }
344
345 void PluginSet::copy(int64_t start, int64_t end, FileXML *file)
346 {
347         file->tag.set_title("PLUGINSET");
348         file->tag.set_property("RECORD", record);
349         file->append_tag();
350         file->append_newline();
351
352         for(Plugin *current = (Plugin*)first; current; current = (Plugin*)NEXT)
353         {
354                 current->copy(start, end, file);
355         }
356
357         file->tag.set_title("/PLUGINSET");
358         file->append_tag();
359         file->append_newline();
360 }
361
362 void PluginSet::save(FileXML *file)
363 {
364         copy(0, length(), file);
365 }
366
367 void PluginSet::load(FileXML *file, uint32_t load_flags)
368 {
369         int result = 0;
370 // Current plugin being amended
371         Plugin *plugin = (Plugin*)first;
372         int64_t startproject = 0;
373
374         record = file->tag.get_property("RECORD", record);
375         do{
376                 result = file->read_tag();
377
378
379                 if(!result)
380                 {
381                         if(file->tag.title_is("/PLUGINSET"))
382                         {
383                                 result = 1;
384                         }
385                         else
386                         if(file->tag.title_is("PLUGIN"))
387                         {
388                                 int64_t length = file->tag.get_property("LENGTH", (int64_t)0);
389                                 int plugin_type = file->tag.get_property("TYPE", 1);
390                                 char title[BCTEXTLEN];
391                                 title[0] = 0;
392                                 file->tag.get_property("TITLE", title);
393                                 SharedLocation shared_location;
394                                 shared_location.load(file);
395
396
397                                 if(load_flags & LOAD_EDITS)
398                                 {
399                                         plugin = insert_plugin(title,
400                                                 startproject,
401                                                 length,
402                                                 plugin_type,
403                                                 &shared_location,
404                                                 0,
405                                                 0);
406                                         plugin->load(file);
407                                         startproject += length;
408                                 }
409                                 else
410                                 if(load_flags & LOAD_AUTOMATION)
411                                 {
412                                         if(plugin)
413                                         {
414                                                 plugin->load(file);
415                                                 plugin = (Plugin*)plugin->next;
416                                         }
417                                 }
418                         }
419                 }
420         } while(!result);
421
422         optimize();
423 }
424
425 int PluginSet::optimize()
426 {
427         int result = 1;
428         Plugin *current_edit;
429
430
431 // Delete keyframes out of range
432         for(current_edit = (Plugin*)first;
433                 current_edit;
434                 current_edit = (Plugin*)current_edit->next)
435         {
436                 current_edit->keyframes->default_auto->position = 0;
437                 for(KeyFrame *current_keyframe = (KeyFrame*)current_edit->keyframes->last;
438                         current_keyframe; )
439                 {
440                         KeyFrame *previous_keyframe = (KeyFrame*)current_keyframe->previous;
441                         if(current_keyframe->position >
442                                 current_edit->startproject + current_edit->length ||
443                                 current_keyframe->position < current_edit->startproject)
444                         {
445                                 delete current_keyframe;
446                         }
447                         current_keyframe = previous_keyframe;
448                 }
449         }
450
451 // Insert silence between plugins
452         for(Plugin *current = (Plugin*)last; current; current = (Plugin*)PREVIOUS)
453         {
454                 if(current->previous)
455                 {
456                         Plugin *previous = (Plugin*)PREVIOUS;
457
458                         if(current->startproject -
459                                 previous->startproject -
460                                 previous->length > 0)
461                         {
462                                 Plugin *new_plugin = (Plugin*)create_edit();
463                                 insert_before(current, new_plugin);
464                                 new_plugin->startproject = previous->startproject +
465                                         previous->length;
466                                 new_plugin->length = current->startproject -
467                                         previous->startproject -
468                                         previous->length;
469                         }
470                 }
471                 else
472                 if(current->startproject > 0)
473                 {
474                         Plugin *new_plugin = (Plugin*)create_edit();
475                         insert_before(current, new_plugin);
476                         new_plugin->length = current->startproject;
477                 }
478         }
479
480
481 // delete 0 length plugins
482         while(result)
483         {
484                 result = 0;
485
486                 for(current_edit = (Plugin*)first; !result && current_edit; ) {
487                         Plugin* next = (Plugin*)current_edit->next;
488                         if(current_edit->length == 0) {
489                                 delete current_edit;
490                                 result = 1;
491                         }
492                         current_edit = next;
493                 }
494
495
496 // merge identical plugins with same keyframes
497                 for( current_edit = (Plugin*)first;
498                         !result && current_edit && current_edit->next; ) {
499                         Plugin *next_edit = (Plugin*)current_edit->next;
500
501 // plugins identical
502                         if(next_edit->identical(current_edit)) {
503                                 current_edit->length += next_edit->length;
504 // Merge keyframes
505                                 for(KeyFrame *source = (KeyFrame*)next_edit->keyframes->first;
506                                         source;
507                                         source = (KeyFrame*)source->next) {
508                                         KeyFrame *dest = new KeyFrame(edl, current_edit->keyframes);
509                                         *dest = *source;
510                                         current_edit->keyframes->append(dest);
511                                 }
512                                 remove(next_edit);
513                                 result = 1;
514                                 continue;
515                         }
516
517                         current_edit = next_edit;
518
519 // delete last edit if 0 length or silence
520                         if( last && (last->silence() || !last->length) ) {
521                                 delete last;
522                                 result = 1;
523                         }
524                 }
525         }
526
527         return 0;
528 }
529
530
531
532
533
534 void PluginSet::dump(FILE *fp)
535 {
536         fprintf(fp,"   PLUGIN_SET:\n");
537         for(Plugin *current = (Plugin*)first; current; current =  (Plugin*)NEXT)
538                 current->dump(fp);
539 }