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