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