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