rework keyframe hide popup, keyframe auto render, textbox set_selection wide text
[goodguy/history.git] / cinelerra-5.1 / cinelerra / track.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "asset.h"
22 #include "autoconf.h"
23 #include "automation.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 "floatauto.h"
32 #include "floatautos.h"
33 #include "keyframe.h"
34 #include "labels.h"
35 #include "localsession.h"
36 #include "module.h"
37 #include "patch.h"
38 #include "patchbay.h"
39 #include "plugin.h"
40 #include "pluginset.h"
41 #include "mainsession.h"
42 #include "theme.h"
43 #include "intautos.h"
44 #include "track.h"
45 #include "trackcanvas.h"
46 #include "tracks.h"
47 #include "transition.h"
48 #include "transportque.inc"
49 #include "vedit.h"
50 #include "vframe.h"
51 #include <string.h>
52
53
54 Track::Track(EDL *edl, Tracks *tracks) : ListItem<Track>()
55 {
56         this->edl = edl;
57         this->tracks = tracks;
58         y_pixel = 0;
59         expand_view = 0;
60         draw = 1;
61         gang = 1;
62         title[0] = 0;
63         record = 1;
64         play = 1;
65         nudge = 0;
66         track_w = edl->session->output_w;
67         track_h = edl->session->output_h;
68         id = EDL::next_id();
69 }
70
71 Track::~Track()
72 {
73         delete automation;
74         delete edits;
75         plugin_set.remove_all_objects();
76 }
77
78 void Track::create_objects()
79 {
80 }
81
82
83 int Track::copy_settings(Track *track)
84 {
85         this->expand_view = track->expand_view;
86         this->draw = track->draw;
87         this->gang = track->gang;
88         this->record = track->record;
89         this->nudge = track->nudge;
90         this->play = track->play;
91         this->track_w = track->track_w;
92         this->track_h = track->track_h;
93         strcpy(this->title, track->title);
94         return 0;
95 }
96
97 int Track::get_id()
98 {
99         return id;
100 }
101
102
103 int Track::load_defaults(BC_Hash *defaults)
104 {
105         return 0;
106 }
107
108 void Track::equivalent_output(Track *track, double *result)
109 {
110         if(data_type != track->data_type ||
111                 track_w != track->track_w ||
112                 track_h != track->track_h ||
113                 play != track->play ||
114                 nudge != track->nudge)
115                 *result = 0;
116
117 // Convert result to track units
118         int64_t result2 = -1;
119         automation->equivalent_output(track->automation, &result2);
120         edits->equivalent_output(track->edits, &result2);
121
122         int plugin_sets = MIN(plugin_set.total, track->plugin_set.total);
123 // Test existing plugin sets
124         for(int i = 0; i < plugin_sets; i++)
125         {
126                 plugin_set.values[i]->equivalent_output(
127                         track->plugin_set.values[i],
128                         &result2);
129         }
130
131 // New EDL has more plugin sets.  Get starting plugin in new plugin sets
132         for(int i = plugin_sets; i < plugin_set.total; i++)
133         {
134                 Plugin *current = plugin_set.values[i]->get_first_plugin();
135                 if(current)
136                 {
137                         if(result2 < 0 || current->startproject < result2)
138                                 result2 = current->startproject;
139                 }
140         }
141
142 // New EDL has fewer plugin sets.  Get starting plugin in old plugin set
143         for(int i = plugin_sets; i < track->plugin_set.total; i++)
144         {
145                 Plugin *current = track->plugin_set.values[i]->get_first_plugin();
146                 if(current)
147                 {
148                         if(result2 < 0 || current->startproject < result2)
149                                 result2 = current->startproject;
150                 }
151         }
152
153 // Number of plugin sets differs but somehow we didn't find the start of the
154 // change.  Assume 0
155         if(track->plugin_set.total != plugin_set.total && result2 < 0)
156                 result2 = 0;
157
158         if(result2 >= 0 &&
159                 (*result < 0 || from_units(result2) < *result))
160                 *result = from_units(result2);
161 }
162
163
164 int Track::is_synthesis(int64_t position,
165         int direction)
166 {
167         int is_synthesis = 0;
168         for(int i = 0; i < plugin_set.total; i++)
169         {
170                 Plugin *plugin = get_current_plugin(position,
171                         i,
172                         direction,
173                         0,
174                         0);
175                 if(plugin)
176                 {
177 // Assume data from a shared track is synthesized
178                         if(plugin->plugin_type == PLUGIN_SHAREDMODULE)
179                                 is_synthesis = 1;
180                         else
181                                 is_synthesis = plugin->is_synthesis(position,
182                                         direction);
183
184 //printf("Track::is_synthesis %d %d\n", __LINE__, is_synthesis);
185                         if(is_synthesis) break;
186                 }
187         }
188         return is_synthesis;
189 }
190
191 void Track::copy_from(Track *track)
192 {
193         copy_settings(track);
194         edits->copy_from(track->edits);
195         for(int i = 0; i < this->plugin_set.total; i++)
196                 delete this->plugin_set.values[i];
197         this->plugin_set.remove_all_objects();
198
199         for(int i = 0; i < track->plugin_set.total; i++)
200         {
201                 PluginSet *new_plugin_set = plugin_set.append(new PluginSet(edl, this));
202                 new_plugin_set->copy_from(track->plugin_set.values[i]);
203         }
204         automation->copy_from(track->automation);
205         this->track_w = track->track_w;
206         this->track_h = track->track_h;
207 }
208
209 Track& Track::operator=(Track& track)
210 {
211 printf("Track::operator= 1\n");
212         copy_from(&track);
213         return *this;
214 }
215
216 int Track::vertical_span(Theme *theme)
217 {
218         int result = 0;
219         if(expand_view)
220                 result = edl->local_session->zoom_track +
221                         plugin_set.total *
222                         theme->get_image("plugin_bg_data")->get_h();
223         else
224                 result = edl->local_session->zoom_track;
225
226         if(edl->session->show_titles)
227                 result += theme->get_image("title_bg_data")->get_h();
228
229         return result;
230 }
231
232 double Track::get_length()
233 {
234         double total_length = 0;
235         double length = 0;
236
237 // Test edits
238         if(edits->last)
239         {
240                 length = from_units(edits->last->startproject + edits->last->length);
241                 if(length > total_length) total_length = length;
242         }
243
244 // Test plugins
245         for(int i = 0; i < plugin_set.total; i++)
246         {
247                 if( !plugin_set.values[i]->last ) continue;
248                 length = from_units(plugin_set.values[i]->last->startproject);
249                 if(length > total_length) total_length = length;
250         }
251
252 // Test keyframes
253         length = from_units(automation->get_length());
254         if(length > total_length) total_length = length;
255
256
257         return total_length;
258 }
259
260 int Track::has_speed()
261 {
262         FloatAutos *autos = (FloatAutos*)automation->autos[AUTOMATION_SPEED];
263         if(autos)
264         {
265                 if(autos->first)
266                 {
267                         for(FloatAuto *current = (FloatAuto*)autos->first;
268                                 current;
269                                 current = (FloatAuto*)current->next)
270                         {
271                                 if(!EQUIV(current->get_value(), 1.0) ||
272                                         !EQUIV(current->get_control_in_value(), 0.0) ||
273                                         !EQUIV(current->get_control_out_value(), 0.0))
274                                 {
275                                         return 1;
276                                 }
277                         }
278                 }
279         }
280
281         return 0;
282 }
283
284
285
286 void Track::get_source_dimensions(double position, int &w, int &h)
287 {
288         int64_t native_position = to_units(position, 0);
289         for(Edit *current = edits->first; current; current = NEXT)
290         {
291                 if(current->startproject <= native_position &&
292                         current->startproject + current->length > native_position &&
293                         current->asset)
294                 {
295                         w = current->asset->width;
296                         h = current->asset->height;
297                         return;
298                 }
299         }
300 }
301
302
303 int64_t Track::horizontal_span()
304 {
305         return (int64_t)(get_length() *
306                 edl->session->sample_rate /
307                 edl->local_session->zoom_sample +
308                 0.5);
309 }
310
311
312 int Track::load(FileXML *file, int track_offset, uint32_t load_flags)
313 {
314         int result = 0;
315         int current_plugin = 0;
316
317
318         record = file->tag.get_property("RECORD", record);
319         play = file->tag.get_property("PLAY", play);
320         gang = file->tag.get_property("GANG", gang);
321         draw = file->tag.get_property("DRAW", draw);
322         nudge = file->tag.get_property("NUDGE", nudge);
323         expand_view = file->tag.get_property("EXPAND", expand_view);
324         track_w = file->tag.get_property("TRACK_W", track_w);
325         track_h = file->tag.get_property("TRACK_H", track_h);
326
327         load_header(file, load_flags);
328
329         do{
330                 result = file->read_tag();
331
332                 if(!result)
333                 {
334                         if(file->tag.title_is("/TRACK"))
335                         {
336                                 result = 1;
337                         }
338                         else
339                         if(file->tag.title_is("TITLE"))
340                         {
341                                 file->read_text_until("/TITLE", title, BCTEXTLEN);
342                         }
343                         else
344                         if(load_flags && automation->load(file)
345                         /* strstr(file->tag.get_title(), "AUTOS") */)
346                         {
347                                 ;
348                         }
349                         else
350                         if(file->tag.title_is("EDITS"))
351                         {
352                                 if(load_flags & LOAD_EDITS)
353                                         edits->load(file, track_offset);
354                         }
355                         else
356                         if(file->tag.title_is("PLUGINSET"))
357                         {
358                                 if(load_flags & LOAD_EDITS)
359                                 {
360                                         PluginSet *plugin_set = new PluginSet(edl, this);
361                                         this->plugin_set.append(plugin_set);
362                                         plugin_set->load(file, load_flags);
363                                 }
364                                 else
365                                 if(load_flags & LOAD_AUTOMATION)
366                                 {
367                                         if(current_plugin < this->plugin_set.total)
368                                         {
369                                                 PluginSet *plugin_set = this->plugin_set.values[current_plugin];
370                                                 plugin_set->load(file, load_flags);
371                                                 current_plugin++;
372                                         }
373                                 }
374                         }
375                         else
376                                 load_derived(file, load_flags);
377                 }
378         }while(!result);
379
380
381
382         return 0;
383 }
384
385 void Track::insert_asset(Asset *asset,
386         EDL *nested_edl,
387         double length,
388         double position,
389         int track_number)
390 {
391         edits->insert_asset(asset,
392                 nested_edl,
393                 to_units(length, 1),
394                 to_units(position, 0),
395                 track_number);
396 }
397
398 // Insert data
399
400 // Default keyframes: We don't replace default keyframes in pasting but
401 // when inserting the first EDL of a load operation we need to replace
402 // the default keyframes.
403
404 // Plugins:  This is an arbitrary behavior
405 //
406 // 1) No plugin in source track: Paste silence into destination
407 // plugin sets.
408 // 2) Plugin in source track: plugin in source track is inserted into
409 // existing destination track plugin sets, new sets being added when
410 // necessary.
411
412 void Track::insert_track(Track *track,
413         double position,
414         int replace_default,
415         int edit_plugins,
416         int edit_autos,
417         double edl_length)
418 {
419 // Calculate minimum length of data to pad.
420         int64_t min_length = to_units(
421                 MAX(edl_length, track->get_length()),
422                 1);
423 //printf("Track::insert_track %d %s %jd\n", __LINE__, title, min_length);
424
425 // Decide whether to copy settings based on load_mode
426         if(replace_default) copy_settings(track);
427
428         edits->insert_edits(track->edits,
429                 to_units(position, 0),
430                 min_length,
431                 edit_autos);
432
433         if(edit_plugins)
434                 insert_plugin_set(track,
435                         to_units(position, 0),
436                         min_length,
437                         edit_autos);
438
439         if(edit_autos)
440                 automation->insert_track(track->automation,
441                         to_units(position, 0),
442                         min_length,
443                         replace_default);
444
445         optimize();
446
447 }
448
449 // Called by insert_track
450 void Track::insert_plugin_set(Track *track,
451         int64_t position,
452         int64_t min_length,
453         int edit_autos)
454 {
455 // Extend plugins if no incoming plugins
456         if(!track->plugin_set.total)
457         {
458                 shift_effects(position,
459                         min_length,
460                         edit_autos);
461         }
462         else
463         for(int i = 0; i < track->plugin_set.total; i++)
464         {
465                 if(i >= plugin_set.total)
466                         plugin_set.append(new PluginSet(edl, this));
467
468                 plugin_set.values[i]->insert_edits(track->plugin_set.values[i],
469                         position,
470                         min_length,
471                         edit_autos);
472         }
473 }
474
475
476 Plugin* Track::insert_effect(const char *title,
477                 SharedLocation *shared_location,
478                 KeyFrame *default_keyframe,
479                 PluginSet *plugin_set,
480                 double start,
481                 double length,
482                 int plugin_type)
483 {
484         if(!plugin_set)
485         {
486                 plugin_set = new PluginSet(edl, this);
487                 this->plugin_set.append(plugin_set);
488         }
489
490         Plugin *plugin = 0;
491
492 // Position is identical to source plugin
493         if(plugin_type == PLUGIN_SHAREDPLUGIN)
494         {
495                 Track *source_track = tracks->get_item_number(shared_location->module);
496                 if(source_track)
497                 {
498                         Plugin *source_plugin = source_track->get_current_plugin(
499                                 edl->local_session->get_selectionstart(),
500                                 shared_location->plugin,
501                                 PLAY_FORWARD,
502                                 1,
503                                 0);
504
505 // From an attach operation
506                         if(source_plugin)
507                         {
508                                 plugin = plugin_set->insert_plugin(title,
509                                         source_plugin->startproject,
510                                         source_plugin->length,
511                                         plugin_type,
512                                         shared_location,
513                                         default_keyframe,
514                                         1);
515                         }
516                         else
517 // From a drag operation
518                         {
519                                 plugin = plugin_set->insert_plugin(title,
520                                         to_units(start, 0),
521                                         to_units(length, 1),
522                                         plugin_type,
523                                         shared_location,
524                                         default_keyframe,
525                                         1);
526                         }
527                 }
528         }
529         else
530         {
531 // This should be done in the caller
532                 if(EQUIV(length, 0))
533                 {
534                         if(edl->local_session->get_selectionend() >
535                                 edl->local_session->get_selectionstart())
536                         {
537                                 start = edl->local_session->get_selectionstart();
538                                 length = edl->local_session->get_selectionend() - start;
539                         }
540                         else
541                         {
542                                 start = 0;
543                                 length = get_length();
544                         }
545                 }
546 //printf("Track::insert_effect %f %f %d %d\n", start, length, to_units(start, 0),
547 //                      to_units(length, 0));
548
549                 plugin = plugin_set->insert_plugin(title,
550                         to_units(start, 0),
551                         to_units(length, 1),
552                         plugin_type,
553                         shared_location,
554                         default_keyframe,
555                         1);
556         }
557 //printf("Track::insert_effect 2 %f %f\n", start, length);
558
559         expand_view = 1;
560         return plugin;
561 }
562
563 void Track::move_plugins_up(PluginSet *plugin_set)
564 {
565         for(int i = 0; i < this->plugin_set.total; i++)
566         {
567                 if(this->plugin_set.values[i] == plugin_set)
568                 {
569                         if(i == 0) break;
570
571                         PluginSet *temp = this->plugin_set.values[i - 1];
572                         this->plugin_set.values[i - 1] = this->plugin_set.values[i];
573                         this->plugin_set.values[i] = temp;
574
575                         SharedLocation old_location, new_location;
576                         new_location.module = old_location.module = tracks->number_of(this);
577                         old_location.plugin = i;
578                         new_location.plugin = i - 1;
579                         tracks->change_plugins(old_location, new_location, 1);
580                         break;
581                 }
582         }
583 }
584
585 void Track::move_plugins_down(PluginSet *plugin_set)
586 {
587         for(int i = 0; i < this->plugin_set.total; i++)
588         {
589                 if(this->plugin_set.values[i] == plugin_set)
590                 {
591                         if(i == this->plugin_set.total - 1) break;
592
593                         PluginSet *temp = this->plugin_set.values[i + 1];
594                         this->plugin_set.values[i + 1] = this->plugin_set.values[i];
595                         this->plugin_set.values[i] = temp;
596
597                         SharedLocation old_location, new_location;
598                         new_location.module = old_location.module = tracks->number_of(this);
599                         old_location.plugin = i;
600                         new_location.plugin = i + 1;
601                         tracks->change_plugins(old_location, new_location, 1);
602                         break;
603                 }
604         }
605 }
606
607
608 void Track::remove_asset(Indexable *asset)
609 {
610         for(Edit *edit = edits->first; edit; edit = edit->next)
611         {
612                 if(asset->is_asset &&
613                         edit->asset &&
614                         edit->asset == (Asset*)asset)
615                 {
616                         edit->asset = 0;
617                 }
618                 else
619                 if(!asset->is_asset &&
620                         edit->nested_edl &&
621                         edit->nested_edl == (EDL*)asset)
622                 {
623                         edit->nested_edl = 0;
624                 }
625         }
626         optimize();
627 }
628
629 void Track::remove_pluginset(PluginSet *plugin_set)
630 {
631         int i;
632         for(i = 0; i < this->plugin_set.total; i++)
633                 if(plugin_set == this->plugin_set.values[i]) break;
634
635         this->plugin_set.remove_object(plugin_set);
636         for(i++ ; i < this->plugin_set.total; i++)
637         {
638                 SharedLocation old_location, new_location;
639                 new_location.module = old_location.module = tracks->number_of(this);
640                 old_location.plugin = i;
641                 new_location.plugin = i - 1;
642                 tracks->change_plugins(old_location, new_location, 0);
643         }
644 }
645
646 void Track::shift_keyframes(int64_t position, int64_t length)
647 {
648         automation->paste_silence(position, position + length);
649 // Effect keyframes are shifted in shift_effects
650 }
651
652 void Track::shift_effects(int64_t position, int64_t length, int edit_autos)
653 {
654         for(int i = 0; i < plugin_set.total; i++)
655         {
656                 plugin_set.values[i]->shift_effects(position, length, edit_autos);
657         }
658 }
659
660 void Track::detach_effect(Plugin *plugin)
661 {
662 //printf("Track::detach_effect 1\n");
663         for(int i = 0; i < plugin_set.total; i++)
664         {
665                 PluginSet *plugin_set = this->plugin_set.values[i];
666                 for(Plugin *dest = (Plugin*)plugin_set->first;
667                         dest;
668                         dest = (Plugin*)dest->next)
669                 {
670                         if(dest == plugin)
671                         {
672                                 int64_t start = plugin->startproject;
673                                 int64_t end = plugin->startproject + plugin->length;
674
675                                 plugin_set->clear(start, end, 1);
676                                 optimize();
677 //printf("Track::detach_effect 2 %d\n", plugin_set->length());
678 // Delete 0 length pluginsets
679                                 return;
680                         }
681                 }
682         }
683 }
684
685 void Track::resample(double old_rate, double new_rate)
686 {
687         edits->resample(old_rate, new_rate);
688         automation->resample(old_rate, new_rate);
689         for(int i = 0; i < plugin_set.total; i++)
690                 plugin_set.values[i]->resample(old_rate, new_rate);
691         nudge = (int64_t)(nudge * new_rate / old_rate);
692 }
693
694 void Track::detach_shared_effects(int module)
695 {
696         for(int i = 0; i < plugin_set.size(); i++) {
697                 PluginSet *plugin_set = this->plugin_set.get(i);
698                 for(Plugin *dest = (Plugin*)plugin_set->first; dest; ) {
699                         if( (dest->plugin_type == PLUGIN_SHAREDPLUGIN ||
700                                 dest->plugin_type == PLUGIN_SHAREDMODULE) &&
701                                 dest->shared_location.module == module ) {
702                                 int64_t start = dest->startproject;
703                                 int64_t end = dest->startproject + dest->length;
704                                 plugin_set->clear(start, end, 1);
705                         }
706
707                         if(dest) dest = (Plugin*)dest->next;
708                 }
709         }
710         optimize();
711 }
712
713
714 void Track::optimize()
715 {
716         edits->optimize();
717         for(int i = 0; i < plugin_set.total; ) {
718                 PluginSet *plugin_set = this->plugin_set.values[i];
719                 plugin_set->optimize();
720 //printf("Track::optimize %d\n", plugin_set.values[i]->total());
721 // new definition of empty track...
722                 if( !plugin_set->last ||
723                     (plugin_set->last == plugin_set->first &&
724                      plugin_set->last->silence()) ) {
725                         remove_pluginset(plugin_set);
726                         continue;
727                 }
728                 ++i;
729         }
730 }
731
732 Plugin* Track::get_current_plugin(double position,
733         int plugin_set,
734         int direction,
735         int convert_units,
736         int use_nudge)
737 {
738         Plugin *current;
739         if(convert_units) position = to_units(position, 0);
740         if(use_nudge) position += nudge;
741
742         if(plugin_set >= this->plugin_set.total || plugin_set < 0) return 0;
743
744 //printf("Track::get_current_plugin 1 %d %d %d\n", position, this->plugin_set.total, direction);
745         if(direction == PLAY_FORWARD)
746         {
747                 for(current = (Plugin*)this->plugin_set.values[plugin_set]->last;
748                         current;
749                         current = (Plugin*)PREVIOUS)
750                 {
751 // printf("Track::get_current_plugin 2 %d %ld %ld\n",
752 // current->startproject,
753 // current->startproject + current->length,
754 // position);
755                         if(current->startproject <= position &&
756                                 current->startproject + current->length > position)
757                         {
758                                 return current;
759                         }
760                 }
761         }
762         else
763         if(direction == PLAY_REVERSE)
764         {
765                 for(current = (Plugin*)this->plugin_set.values[plugin_set]->first;
766                         current;
767                         current = (Plugin*)NEXT)
768                 {
769                         if(current->startproject < position &&
770                                 current->startproject + current->length >= position)
771                         {
772                                 return current;
773                         }
774                 }
775         }
776
777         return 0;
778 }
779
780 Plugin* Track::get_current_transition(double position,
781         int direction,
782         int convert_units,
783         int use_nudge)
784 {
785         Edit *current;
786         Plugin *result = 0;
787         if(convert_units) position = to_units(position, 0);
788         if(use_nudge) position += nudge;
789
790         if(direction == PLAY_FORWARD)
791         {
792                 for(current = edits->last; current; current = PREVIOUS)
793                 {
794                         if(current->startproject <= position && current->startproject + current->length > position)
795                         {
796 //printf("Track::get_current_transition %p\n", current->transition);
797                                 if(current->transition && position < current->startproject + current->transition->length)
798                                 {
799                                         result = current->transition;
800                                         break;
801                                 }
802                         }
803                 }
804         }
805         else
806         if(direction == PLAY_REVERSE)
807         {
808                 for(current = edits->first; current; current = NEXT)
809                 {
810                         if(current->startproject < position && current->startproject + current->length >= position)
811                         {
812                                 if(current->transition && position <= current->startproject + current->transition->length)
813                                 {
814                                         result = current->transition;
815                                         break;
816                                 }
817                         }
818                 }
819         }
820
821         return result;
822 }
823
824 void Track::synchronize_params(Track *track)
825 {
826         for(Edit *this_edit = edits->first, *that_edit = track->edits->first;
827                 this_edit && that_edit;
828                 this_edit = this_edit->next, that_edit = that_edit->next)
829         {
830                 this_edit->synchronize_params(that_edit);
831         }
832
833         for(int i = 0; i < plugin_set.total && i < track->plugin_set.total; i++)
834                 plugin_set.values[i]->synchronize_params(track->plugin_set.values[i]);
835
836         automation->copy_from(track->automation);
837         this->nudge = track->nudge;
838 }
839
840
841
842
843
844 int Track::dump(FILE *fp)
845 {
846         fprintf(fp,"   Data type %d\n", data_type);
847         fprintf(fp,"   Title %s\n", title);
848         fprintf(fp,"   Edits:\n");
849         for(Edit* current = edits->first; current; current = NEXT)
850         {
851                 current->dump(fp);
852         }
853         automation->dump(fp);
854         fprintf(fp,"   Plugin Sets: %d\n", plugin_set.total);
855
856         for(int i = 0; i < plugin_set.total; i++)
857                 plugin_set.values[i]->dump(fp);
858 //printf("Track::dump 2\n");
859         return 0;
860 }
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882 Track::Track() : ListItem<Track>()
883 {
884         y_pixel = 0;
885 }
886
887 // ======================================== accounting
888
889 int Track::number_of()
890 {
891         return tracks->number_of(this);
892 }
893
894
895
896
897
898
899
900
901
902
903
904
905
906 // ================================================= editing
907
908 int Track::select_auto(AutoConf *auto_conf, int cursor_x, int cursor_y)
909 {
910         return 0;
911 }
912
913 int Track::move_auto(AutoConf *auto_conf, int cursor_x, int cursor_y, int shift_down)
914 {
915         return 0;
916 }
917
918 int Track::release_auto()
919 {
920         return 0;
921 }
922
923 // used for copying automation alone
924 int Track::copy_automation(double selectionstart,
925         double selectionend,
926         FileXML *file,
927         int default_only,
928         int active_only)
929 {
930         int64_t start = to_units(selectionstart, 0);
931         int64_t end = to_units(selectionend, 1);
932
933         file->tag.set_title("TRACK");
934 // Video or audio
935     save_header(file);
936         file->append_tag();
937         file->append_newline();
938
939         automation->copy(start, end, file, default_only, active_only);
940
941         if(edl->session->auto_conf->plugins)
942         {
943                 for(int i = 0; i < plugin_set.total; i++)
944                 {
945
946                         plugin_set.values[i]->copy_keyframes(start,
947                                 end,
948                                 file,
949                                 default_only,
950                                 active_only);
951                 }
952         }
953
954         file->tag.set_title("/TRACK");
955         file->append_tag();
956         file->append_newline();
957         file->append_newline();
958         file->append_newline();
959         file->append_newline();
960
961         return 0;
962 }
963
964 int Track::paste_automation(double selectionstart,
965         double total_length,
966         double frame_rate,
967         int64_t sample_rate,
968         FileXML *file,
969         int default_only,
970         int active_only)
971 {
972 // Only used for pasting automation alone.
973         int64_t start;
974         int64_t length;
975         int result;
976         double scale;
977         int current_pluginset;
978
979         if(data_type == TRACK_AUDIO)
980                 scale = edl->session->sample_rate / sample_rate;
981         else
982                 scale = edl->session->frame_rate / frame_rate;
983
984         total_length *= scale;
985         start = to_units(selectionstart, 0);
986         length = to_units(total_length, 1);
987         result = 0;
988         current_pluginset = 0;
989 //printf("Track::paste_automation 1\n");
990
991         while(!result)
992         {
993                 result = file->read_tag();
994
995                 if(!result)
996                 {
997                         if(file->tag.title_is("/TRACK"))
998                                 result = 1;
999                         else
1000                         if(automation->paste(start, length, scale, file,
1001                                         default_only, active_only, 0)) {}
1002                         else if(file->tag.title_is("PLUGINSET")) {
1003                                 if(current_pluginset < plugin_set.total) {
1004                                         plugin_set.values[current_pluginset]->
1005                                                 paste_keyframes(start, length, file,
1006                                                 default_only, active_only);
1007                                         current_pluginset++;
1008                                 }
1009                         }
1010                 }
1011         }
1012
1013
1014         return 0;
1015 }
1016
1017 void Track::clear_automation(double selectionstart,
1018         double selectionend,
1019         int shift_autos,
1020         int default_only)
1021 {
1022         int64_t start = to_units(selectionstart, 0);
1023         int64_t end = to_units(selectionend, 1);
1024
1025         automation->clear(start, end, edl->session->auto_conf, 0);
1026
1027         if(edl->session->auto_conf->plugins)
1028         {
1029                 for(int i = 0; i < plugin_set.total; i++)
1030                 {
1031                         plugin_set.values[i]->clear_keyframes(start, end);
1032                 }
1033         }
1034
1035 }
1036
1037 void Track::set_automation_mode(double selectionstart,
1038         double selectionend,
1039         int mode)
1040 {
1041         int64_t start = to_units(selectionstart, 0);
1042         int64_t end = to_units(selectionend, 1);
1043
1044         automation->set_automation_mode(start, end, mode, edl->session->auto_conf);
1045 }
1046
1047
1048
1049
1050 int Track::copy(double start,
1051         double end,
1052         FileXML *file,
1053         const char *output_path)
1054 {
1055 // Use a copy of the selection in converted units
1056 // So copy_automation doesn't reconvert.
1057         int64_t start_unit = to_units(start, 0);
1058         int64_t end_unit = to_units(end, 1);
1059
1060
1061
1062
1063         file->tag.set_title("TRACK");
1064         file->tag.set_property("RECORD", record);
1065         file->tag.set_property("NUDGE", nudge);
1066         file->tag.set_property("PLAY", play);
1067         file->tag.set_property("GANG", gang);
1068         file->tag.set_property("DRAW", draw);
1069         file->tag.set_property("EXPAND", expand_view);
1070         file->tag.set_property("TRACK_W", track_w);
1071         file->tag.set_property("TRACK_H", track_h);
1072         save_header(file);
1073         file->append_tag();
1074         file->append_newline();
1075         save_derived(file);
1076
1077         file->tag.set_title("TITLE");
1078         file->append_tag();
1079         file->append_text(title);
1080         file->tag.set_title("/TITLE");
1081         file->append_tag();
1082         file->append_newline();
1083
1084 //      if(data_type == TRACK_AUDIO)
1085 //              file->tag.set_property("TYPE", "AUDIO");
1086 //      else
1087 //              file->tag.set_property("TYPE", "VIDEO");
1088 //
1089 //      file->append_tag();
1090 //      file->append_newline();
1091
1092         edits->copy(start_unit, end_unit, file, output_path);
1093
1094         AutoConf auto_conf;
1095         auto_conf.set_all(1);
1096         automation->copy(start_unit, end_unit, file, 0, 0);
1097
1098
1099         for(int i = 0; i < plugin_set.total; i++)
1100         {
1101                 plugin_set.values[i]->copy(start_unit, end_unit, file);
1102         }
1103
1104         copy_derived(start_unit, end_unit, file);
1105
1106         file->tag.set_title("/TRACK");
1107         file->append_tag();
1108         file->append_newline();
1109         file->append_newline();
1110         file->append_newline();
1111         file->append_newline();
1112
1113         return 0;
1114 }
1115
1116 int Track::copy_assets(double start,
1117         double end,
1118         ArrayList<Asset*> *asset_list)
1119 {
1120         int i, result = 0;
1121
1122         start = to_units(start, 0);
1123         end = to_units(end, 1);
1124
1125         Edit *current = edits->editof((int64_t)start, PLAY_FORWARD, 0);
1126
1127 // Search all edits
1128         while(current && current->startproject < end)
1129         {
1130 // Check for duplicate assets
1131                 if(current->asset)
1132                 {
1133                         for(i = 0, result = 0; i < asset_list->total; i++)
1134                         {
1135                                 if(asset_list->values[i] == current->asset) result = 1;
1136                         }
1137 // append pointer to new asset
1138                         if(!result) asset_list->append(current->asset);
1139                 }
1140
1141                 current = NEXT;
1142         }
1143
1144         return 0;
1145 }
1146
1147
1148
1149
1150
1151 int Track::clear(double start,
1152         double end,
1153         int edit_edits,
1154         int edit_labels,
1155         int edit_plugins,
1156         int edit_autos,
1157         int convert_units,
1158         Edits *trim_edits)
1159 {
1160 // Edits::move_auto calls this routine after the units are converted to the track
1161 // format.
1162 //printf("Track::clear 1 %d %d %d\n", edit_edits, edit_labels, edit_plugins);
1163         if(convert_units)
1164         {
1165                 start = to_units(start, 0);
1166                 end = to_units(end, 1);
1167         }
1168
1169
1170         if(edit_autos)
1171                 automation->clear((int64_t)start, (int64_t)end, 0, 1);
1172
1173         if(edit_plugins)
1174         {
1175                 for(int i = 0; i < plugin_set.total; i++)
1176                 {
1177                         if(!trim_edits || trim_edits == (Edits*)plugin_set.values[i])
1178                                 plugin_set.values[i]->clear((int64_t)start, (int64_t)end, edit_autos);
1179                 }
1180         }
1181
1182         if(edit_edits)
1183                 edits->clear((int64_t)start, (int64_t)end);
1184         return 0;
1185 }
1186
1187 int Track::clear_handle(double start,
1188         double end,
1189         int clear_labels,
1190         int clear_plugins,
1191         int edit_autos,
1192         double &distance)
1193 {
1194         edits->clear_handle(start, end, clear_plugins, edit_autos, distance);
1195         return 0;
1196 }
1197
1198 int Track::popup_transition(int cursor_x, int cursor_y)
1199 {
1200         return 0;
1201 }
1202
1203
1204
1205 int Track::modify_edithandles(double oldposition,
1206         double newposition,
1207         int currentend,
1208         int handle_mode,
1209         int edit_labels,
1210         int edit_plugins,
1211         int edit_autos)
1212 {
1213         edits->modify_handles(oldposition,
1214                 newposition,
1215                 currentend,
1216                 handle_mode,
1217                 1,
1218                 edit_labels,
1219                 edit_plugins,
1220                 edit_autos,
1221                 0);
1222
1223
1224         return 0;
1225 }
1226
1227 int Track::modify_pluginhandles(double oldposition,
1228         double newposition,
1229         int currentend,
1230         int handle_mode,
1231         int edit_labels,
1232         int edit_autos,
1233         Edits *trim_edits)
1234 {
1235         for(int i = 0; i < plugin_set.total; i++)
1236         {
1237                 if(!trim_edits || trim_edits == (Edits*)plugin_set.values[i])
1238                         plugin_set.values[i]->modify_handles(oldposition,
1239                                 newposition,
1240                                 currentend,
1241                                 handle_mode,
1242 // Don't allow plugin tweeks to affect edits.
1243                                 0,
1244                                 edit_labels,
1245                                 1,
1246                                 edit_autos,
1247                                 trim_edits);
1248         }
1249         return 0;
1250 }
1251
1252
1253 int Track::paste_silence(double start, double end, int edit_plugins, int edit_autos)
1254 {
1255         int64_t start_i = to_units(start, 0);
1256         int64_t end_i = to_units(end, 1);
1257
1258         edits->paste_silence(start_i, end_i);
1259         if(edit_autos) shift_keyframes(start_i, end_i - start_i);
1260         if(edit_plugins) shift_effects(start_i, end_i - start_i, edit_autos);
1261
1262         edits->optimize();
1263         return 0;
1264 }
1265
1266 int Track::select_edit(int cursor_x,
1267         int cursor_y,
1268         double &new_start,
1269         double &new_end)
1270 {
1271         return 0;
1272 }
1273
1274 int Track::scale_time(float rate_scale, int scale_edits, int scale_autos, int64_t start, int64_t end)
1275 {
1276         return 0;
1277 }
1278
1279 void Track::change_plugins(SharedLocation &old_location, SharedLocation &new_location, int do_swap)
1280 {
1281         for(int i = 0; i < plugin_set.total; i++)
1282         {
1283                 for(Plugin *plugin = (Plugin*)plugin_set.values[i]->first;
1284                         plugin;
1285                         plugin = (Plugin*)plugin->next)
1286                 {
1287                         if(plugin->plugin_type == PLUGIN_SHAREDPLUGIN)
1288                         {
1289                                 if(plugin->shared_location == old_location)
1290                                         plugin->shared_location = new_location;
1291                                 else
1292                                 if(do_swap && plugin->shared_location == new_location)
1293                                         plugin->shared_location = old_location;
1294                         }
1295                 }
1296         }
1297 }
1298
1299 void Track::change_modules(int old_location, int new_location, int do_swap)
1300 {
1301         for(int i = 0; i < plugin_set.total; i++)
1302         {
1303                 for(Plugin *plugin = (Plugin*)plugin_set.values[i]->first;
1304                         plugin;
1305                         plugin = (Plugin*)plugin->next)
1306                 {
1307                         if(plugin->plugin_type == PLUGIN_SHAREDPLUGIN ||
1308                                 plugin->plugin_type == PLUGIN_SHAREDMODULE)
1309                         {
1310                                 if(plugin->shared_location.module == old_location)
1311                                         plugin->shared_location.module = new_location;
1312                                 else
1313                                 if(do_swap && plugin->shared_location.module == new_location)
1314                                         plugin->shared_location.module = old_location;
1315                         }
1316                 }
1317         }
1318 }
1319
1320
1321 int Track::playable_edit(int64_t position, int direction)
1322 {
1323         int result = 0;
1324         if(direction == PLAY_REVERSE) position--;
1325         for(Edit *current = edits->first; current && !result; current = NEXT)
1326         {
1327                 if(current->startproject <= position &&
1328                         current->startproject + current->length > position)
1329                 {
1330 //printf("Track::playable_edit %p %p\n", current->transition, current->asset);
1331                         if(current->transition ||
1332                                 current->asset ||
1333                                 current->nested_edl) result = 1;
1334                 }
1335         }
1336         return result;
1337 }
1338
1339
1340 int Track::need_edit(Edit *current, int test_transitions)
1341 {
1342         return ((test_transitions && current->transition) ||
1343                 (!test_transitions && current->asset));
1344 }
1345
1346 int64_t Track::plugin_change_duration(int64_t input_position,
1347         int64_t input_length,
1348         int reverse,
1349         int use_nudge)
1350 {
1351         if(use_nudge) input_position += nudge;
1352         for(int i = 0; i < plugin_set.total; i++)
1353         {
1354                 int64_t new_duration = plugin_set.values[i]->plugin_change_duration(
1355                         input_position,
1356                         input_length,
1357                         reverse);
1358                 if(new_duration < input_length) input_length = new_duration;
1359         }
1360         return input_length;
1361 }
1362
1363 int64_t Track::edit_change_duration(int64_t input_position,
1364         int64_t input_length,
1365         int reverse,
1366         int test_transitions,
1367         int use_nudge)
1368 {
1369         Edit *current;
1370         int64_t edit_length = input_length;
1371         if(use_nudge) input_position += nudge;
1372
1373         if(reverse)
1374         {
1375 // ================================= Reverse playback
1376 // Get first edit on or after position
1377                 for(current = edits->first;
1378                         current && current->startproject + current->length <= input_position;
1379                         current = NEXT)
1380                         ;
1381
1382                 if(current)
1383                 {
1384                         if(current->startproject > input_position)
1385                         {
1386 // Before first edit
1387                                 ;
1388                         }
1389                         else
1390                         if(need_edit(current, test_transitions))
1391                         {
1392 // Over an edit of interest.
1393                                 if(input_position - current->startproject < input_length)
1394                                         edit_length = input_position - current->startproject + 1;
1395                         }
1396                         else
1397                         {
1398 // Over an edit that isn't of interest.
1399 // Search for next edit of interest.
1400                                 for(current = PREVIOUS ;
1401                                         current &&
1402                                         current->startproject + current->length > input_position - input_length &&
1403                                         !need_edit(current, test_transitions);
1404                                         current = PREVIOUS)
1405                                         ;
1406
1407                                         if(current &&
1408                                                 need_edit(current, test_transitions) &&
1409                                                 current->startproject + current->length > input_position - input_length)
1410                         edit_length = input_position - current->startproject - current->length + 1;
1411                         }
1412                 }
1413                 else
1414                 {
1415 // Not over an edit.  Try the last edit.
1416                         current = edits->last;
1417                         if(current &&
1418                                 ((test_transitions && current->transition) ||
1419                                 (!test_transitions && current->asset)))
1420                                 edit_length = input_position - edits->length() + 1;
1421                 }
1422         }
1423         else
1424         {
1425 // =================================== forward playback
1426 // Get first edit on or before position
1427                 for(current = edits->last;
1428                         current && current->startproject > input_position;
1429                         current = PREVIOUS)
1430                         ;
1431
1432                 if(current)
1433                 {
1434                         if(current->startproject + current->length <= input_position)
1435                         {
1436 // Beyond last edit.
1437                                 ;
1438                         }
1439                         else
1440                         if(need_edit(current, test_transitions))
1441                         {
1442 // Over an edit of interest.
1443 // Next edit is going to require a change.
1444                                 if(current->length + current->startproject - input_position < input_length)
1445                                         edit_length = current->startproject + current->length - input_position;
1446                         }
1447                         else
1448                         {
1449 // Over an edit that isn't of interest.
1450 // Search for next edit of interest.
1451                                 for(current = NEXT ;
1452                                         current &&
1453                                         current->startproject < input_position + input_length &&
1454                                         !need_edit(current, test_transitions);
1455                                         current = NEXT)
1456                                         ;
1457
1458                                         if(current &&
1459                                                 need_edit(current, test_transitions) &&
1460                                                 current->startproject < input_position + input_length)
1461                                                 edit_length = current->startproject - input_position;
1462                         }
1463                 }
1464                 else
1465                 {
1466 // Not over an edit.  Try the first edit.
1467                         current = edits->first;
1468                         if(current &&
1469                                 ((test_transitions && current->transition) ||
1470                                 (!test_transitions && current->asset)))
1471                                 edit_length = edits->first->startproject - input_position;
1472                 }
1473         }
1474
1475         if(edit_length < input_length)
1476                 return edit_length;
1477         else
1478                 return input_length;
1479 }
1480
1481 void Track::shuffle_edits(double start, double end, int first_track)
1482 {
1483         ArrayList<Edit*> new_edits;
1484         ArrayList<Label*> new_labels;
1485         int64_t start_units = to_units(start, 0);
1486         int64_t end_units = to_units(end, 0);
1487 // Sample range of all edits selected
1488         //int64_t total_start_units = 0;
1489         //int64_t total_end_units = 0;
1490 // Edit before range
1491         Edit *start_edit = 0;
1492         int have_start_edit = 0;
1493
1494 // Move all edit pointers to list
1495         for(Edit *current = edits->first;
1496                 current; )
1497         {
1498                 if(current->startproject >= start_units &&
1499                         current->startproject + current->length <= end_units)
1500                 {
1501                         if(!have_start_edit) start_edit = current->previous;
1502                         have_start_edit = 1;
1503                         //total_start_units = current->startproject;
1504                         //total_end_units = current->startproject + current->length;
1505                         new_edits.append(current);
1506
1507 // Move label pointers
1508                         if(first_track && edl->session->labels_follow_edits)
1509                         {
1510                                 double start_seconds = from_units(current->startproject);
1511                                 double end_seconds = from_units(current->startproject +
1512                                         current->length);
1513                                 for(Label *label = edl->labels->first;
1514                                         label;
1515                                         label = label->next)
1516                                 {
1517                                         if(label->position >= start_seconds &&
1518                                                 label->position < end_seconds)
1519                                         {
1520                                                 new_labels.append(label);
1521                                                 edl->labels->remove_pointer(label);
1522                                         }
1523                                 }
1524                         }
1525
1526 // Remove edit pointer
1527                         Edit *previous = current;
1528                         current = NEXT;
1529                         edits->remove_pointer(previous);
1530                 }
1531                 else
1532                 {
1533                         current = NEXT;
1534                 }
1535         }
1536
1537 // Insert pointers in random order
1538         while(new_edits.size())
1539         {
1540                 int index = rand() % new_edits.size();
1541                 Edit *edit = new_edits.get(index);
1542                 new_edits.remove_number(index);
1543                 edits->insert_after(start_edit, edit);
1544                 start_edit = edit;
1545
1546 // Recalculate start position
1547 // Save old position for moving labels
1548                 int64_t startproject1 = edit->startproject;
1549                 int64_t startproject2 = 0;
1550                 if(edit->previous)
1551                 {
1552                         edit->startproject =
1553                                 startproject2 =
1554                                 edit->previous->startproject + edit->previous->length;
1555                 }
1556                 else
1557                 {
1558                         edit->startproject = startproject2 = 0;
1559                 }
1560
1561
1562 // Insert label pointers
1563                 if(first_track && edl->session->labels_follow_edits)
1564                 {
1565                         double start_seconds1 = from_units(startproject1);
1566                         double start_seconds2 = from_units(startproject2);
1567                         //double end_seconds1 = from_units(edit->startproject +
1568                         //      edit->length);
1569                         for(int i = new_labels.size() - 1; i >= 0; i--)
1570                         {
1571                                 Label *label = new_labels.get(i);
1572 // Was in old edit position
1573                                 if(label->position >= start_seconds1 &&
1574                                         label->position < start_seconds2)
1575                                 {
1576 // Move to new edit position
1577                                         double position = label->position -
1578                                                 start_seconds1 +
1579                                                 start_seconds2;
1580                                         edl->labels->insert_label(position);
1581                                         new_labels.remove_object_number(i);
1582                                 }
1583                         }
1584                 }
1585
1586
1587         }
1588
1589         optimize();
1590
1591         if(first_track && edl->session->labels_follow_edits)
1592         {
1593                 edl->labels->optimize();
1594         }
1595 }
1596
1597 // exactly the same as shuffle_edits except for 1 line
1598 void Track::reverse_edits(double start, double end, int first_track)
1599 {
1600         ArrayList<Edit*> new_edits;
1601         ArrayList<Label*> new_labels;
1602         int64_t start_units = to_units(start, 0);
1603         int64_t end_units = to_units(end, 0);
1604 // Sample range of all edits selected
1605         //int64_t total_start_units = 0;
1606         //int64_t total_end_units = 0;
1607 // Edit before range
1608         Edit *start_edit = 0;
1609         int have_start_edit = 0;
1610
1611 // Move all edit pointers to list
1612         for(Edit *current = edits->first; current; )
1613         {
1614                 if(current->startproject >= start_units &&
1615                         current->startproject + current->length <= end_units)
1616                 {
1617                         if(!have_start_edit) start_edit = current->previous;
1618                         have_start_edit = 1;
1619                         //total_start_units = current->startproject;
1620                         //total_end_units = current->startproject + current->length;
1621                         new_edits.append(current);
1622
1623 // Move label pointers
1624                         if(first_track && edl->session->labels_follow_edits)
1625                         {
1626                                 double start_seconds = from_units(current->startproject);
1627                                 double end_seconds = from_units(current->startproject +
1628                                         current->length);
1629                                 for(Label *label = edl->labels->first;
1630                                         label;
1631                                         label = label->next)
1632                                 {
1633                                         if(label->position >= start_seconds &&
1634                                                 label->position < end_seconds)
1635                                         {
1636                                                 new_labels.append(label);
1637                                                 edl->labels->remove_pointer(label);
1638                                         }
1639                                 }
1640                         }
1641
1642 // Remove edit pointer
1643                         Edit *previous = current;
1644                         current = NEXT;
1645                         edits->remove_pointer(previous);
1646                 }
1647                 else
1648                 {
1649                         current = NEXT;
1650                 }
1651         }
1652
1653 // Insert pointers in reverse order
1654         while(new_edits.size())
1655         {
1656                 int index = new_edits.size() - 1;
1657                 Edit *edit = new_edits.get(index);
1658                 new_edits.remove_number(index);
1659                 edits->insert_after(start_edit, edit);
1660                 start_edit = edit;
1661
1662 // Recalculate start position
1663 // Save old position for moving labels
1664                 int64_t startproject1 = edit->startproject;
1665                 int64_t startproject2 = 0;
1666                 if(edit->previous)
1667                 {
1668                         edit->startproject =
1669                                 startproject2 =
1670                                 edit->previous->startproject + edit->previous->length;
1671                 }
1672                 else
1673                 {
1674                         edit->startproject = startproject2 = 0;
1675                 }
1676
1677
1678 // Insert label pointers
1679                 if(first_track && edl->session->labels_follow_edits)
1680                 {
1681                         double start_seconds1 = from_units(startproject1);
1682                         double start_seconds2 = from_units(startproject2);
1683                         //double end_seconds1 = from_units(edit->startproject + edit->length);
1684                         for(int i = new_labels.size() - 1; i >= 0; i--)
1685                         {
1686                                 Label *label = new_labels.get(i);
1687 // Was in old edit position
1688                                 if(label->position >= start_seconds1 &&
1689                                         label->position < start_seconds2)
1690                                 {
1691 // Move to new edit position
1692                                         double position = label->position -
1693                                                 start_seconds1 +
1694                                                 start_seconds2;
1695                                         edl->labels->insert_label(position);
1696                                         new_labels.remove_object_number(i);
1697                                 }
1698                         }
1699                 }
1700
1701
1702         }
1703
1704         optimize();
1705
1706         if(first_track && edl->session->labels_follow_edits)
1707         {
1708                 edl->labels->optimize();
1709         }
1710 }
1711
1712 void Track::align_edits(double start,
1713         double end,
1714         ArrayList<double> *times)
1715 {
1716         int64_t start_units = to_units(start, 0);
1717         int64_t end_units = to_units(end, 0);
1718
1719 // If 1st track with data, times is empty & we need to collect the edit times.
1720         if(!times->size())
1721         {
1722                 for(Edit *current = edits->first; current; current = NEXT)
1723                 {
1724                         if(current->startproject >= start_units &&
1725                                 current->startproject + current->length <= end_units)
1726                         {
1727                                 times->append(from_units(current->startproject));
1728                         }
1729                 }
1730         }
1731         else
1732 // All other tracks get silence or cut to align the edits on the times.
1733         {
1734                 int current_time = 0;
1735                 for(Edit *current = edits->first;
1736                         current && current_time < times->size(); )
1737                 {
1738                         if(current->startproject >= start_units &&
1739                                 current->startproject + current->length <= end_units)
1740                         {
1741                                 int64_t desired_startunits = to_units(times->get(current_time), 0);
1742                                 int64_t current_startunits = current->startproject;
1743                                 current = NEXT;
1744
1745
1746                                 if(current_startunits < desired_startunits)
1747                                 {
1748 //printf("Track::align_edits %d\n", __LINE__);
1749                                         edits->paste_silence(current_startunits,
1750                                                 desired_startunits);
1751                                         shift_keyframes(current_startunits,
1752                                                 desired_startunits - current_startunits);
1753                                 }
1754                                 else
1755                                 if(current_startunits > desired_startunits)
1756                                 {
1757                                         edits->clear(desired_startunits,
1758                                                 current_startunits);
1759                                         if(edl->session->autos_follow_edits)
1760                                                 shift_keyframes(desired_startunits,
1761                                                         current_startunits - desired_startunits);
1762                                 }
1763
1764                                 current_time++;
1765                         }
1766                         else
1767                         {
1768                                 current = NEXT;
1769                         }
1770                 }
1771         }
1772
1773         optimize();
1774 }
1775
1776 int Track::purge_asset(Asset *asset)
1777 {
1778         return 0;
1779 }
1780
1781 int Track::asset_used(Asset *asset)
1782 {
1783         Edit* current_edit;
1784         int result = 0;
1785
1786         for(current_edit = edits->first; current_edit; current_edit = current_edit->next)
1787         {
1788                 if(current_edit->asset == asset)
1789                 {
1790                         result++;
1791                 }
1792         }
1793         return result;
1794 }
1795
1796 int Track::is_playable(int64_t position, int direction)
1797 {
1798         return 1;
1799 }
1800
1801
1802 int Track::plugin_used(int64_t position, int64_t direction)
1803 {
1804 //printf("Track::plugin_used 1 %d\n", this->plugin_set.total);
1805         for(int i = 0; i < this->plugin_set.total; i++)
1806         {
1807                 Plugin *current_plugin = get_current_plugin(position,
1808                         i,
1809                         direction,
1810                         0,
1811                         0);
1812
1813 //printf("Track::plugin_used 2 %p %d %d\n", current_plugin, current_plugin->on, current_plugin->plugin_type);
1814                 if(current_plugin &&
1815                         current_plugin->on &&
1816                         current_plugin->plugin_type != PLUGIN_NONE)
1817                 {
1818                         return 1;
1819                 }
1820         }
1821 //printf("Track::plugin_used 3 %p\n", current_plugin);
1822         return 0;
1823 }
1824
1825 // Audio is always rendered through VConsole
1826 int Track::direct_copy_possible(int64_t start, int direction, int use_nudge)
1827 {
1828         return 1;
1829 }
1830
1831 int64_t Track::to_units(double position, int round)
1832 {
1833         return (int64_t)position;
1834 }
1835
1836 double Track::to_doubleunits(double position)
1837 {
1838         return position;
1839 }
1840
1841 double Track::from_units(int64_t position)
1842 {
1843         return (double)position;
1844 }
1845
1846 int Track::plugin_exists(Plugin *plugin)
1847 {
1848         for(int number = 0; number < plugin_set.size(); number++)
1849         {
1850                 PluginSet *ptr = plugin_set.get(number);
1851                 for(Plugin *current_plugin = (Plugin*)ptr->first;
1852                         current_plugin;
1853                         current_plugin = (Plugin*)current_plugin->next)
1854                 {
1855                         if(current_plugin == plugin) return 1;
1856                 }
1857         }
1858
1859         for(Edit *current = edits->first; current; current = NEXT)
1860         {
1861                 if(current->transition &&
1862                         (Plugin*)current->transition == plugin) return 1;
1863         }
1864
1865
1866         return 0;
1867 }
1868