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