4 * Copyright (C) 1997-2012 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "automation.h"
27 #include "awindowgui.h"
28 #include "bccmodels.h"
30 #include "bcsignals.h"
36 #include "edlsession.h"
38 #include "floatauto.h"
39 #include "floatautos.h"
41 #include "indexstate.h"
42 #include "interlacemodes.h"
44 #include "localsession.h"
45 #include "maskautos.h"
49 #include "playbackconfig.h"
50 #include "playabletracks.h"
52 #include "pluginset.h"
53 #include "preferences.h"
54 #include "recordconfig.h"
55 #include "recordlabel.h"
56 #include "sharedlocation.h"
59 #include "transportque.inc"
60 #include "versioninfo.h"
67 EDL::EDL(EDL *parent_edl)
70 this->parent_edl = parent_edl;
85 remove_vwindow_edls();
93 void EDL::create_objects()
95 tracks = new Tracks(this);
96 assets = !parent_edl ? new Assets(this) : parent_edl->assets;
97 session = !parent_edl ? new EDLSession(this) : parent_edl->session;
98 local_session = new LocalSession(this);
99 labels = new Labels(this, "LABELS");
102 EDL& EDL::operator=(EDL &edl)
104 printf("EDL::operator= 1\n");
109 int EDL::load_defaults(BC_Hash *defaults)
112 session->load_defaults(defaults);
114 local_session->load_defaults(defaults);
118 int EDL::save_defaults(BC_Hash *defaults)
121 session->save_defaults(defaults);
123 local_session->save_defaults(defaults);
127 void EDL::boundaries()
129 session->boundaries();
130 local_session->boundaries();
133 int EDL::create_default_tracks()
136 for( int i=0; i<session->video_tracks; ++i ) {
137 tracks->add_video_track(0, 0);
139 for( int i=0; i<session->audio_tracks; ++i ) {
140 tracks->add_audio_track(0, 0);
145 int EDL::load_xml(FileXML *file, uint32_t load_flags)
150 if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
151 remove_vwindow_edls();
155 // Search for start of master EDL.
157 // The parent_edl test caused clip creation to fail since those XML files
158 // contained an EDL tag.
160 // The parent_edl test is required to make EDL loading work because
161 // when loading an EDL the EDL tag is already read by the parent.
165 result = file->read_tag();
167 !file->tag.title_is("XML") &&
168 !file->tag.title_is("EDL"));
170 return result ? result : read_xml(file, load_flags);
173 int EDL::read_xml(FileXML *file, uint32_t load_flags)
176 // Track numbering offset for replacing undo data.
177 int track_offset = 0;
179 // Get path for backups
180 file->tag.get_property("path", path);
183 if( (load_flags & LOAD_ALL) == LOAD_ALL ||
184 (load_flags & LOAD_EDITS) == LOAD_EDITS ) {
185 while(tracks->last) delete tracks->last;
188 if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
190 mixers.remove_all_objects();
193 if( load_flags & LOAD_TIMEBAR ) {
194 while(labels->last) delete labels->last;
195 local_session->unset_inpoint();
196 local_session->unset_outpoint();
199 // This was originally in LocalSession::load_xml
200 if( load_flags & LOAD_SESSION ) {
201 local_session->clipboard_length = 0;
205 result = file->read_tag();
208 if( file->tag.title_is("/XML") ||
209 file->tag.title_is("/EDL") ||
210 file->tag.title_is("/CLIP_EDL") ||
211 file->tag.title_is("/NESTED_EDL") ||
212 file->tag.title_is("/VWINDOW_EDL") ) {
216 if( file->tag.title_is("CLIPBOARD") ) {
217 local_session->clipboard_length =
218 file->tag.get_property("LENGTH", (double)0);
221 if( file->tag.title_is("VIDEO") ) {
222 if( (load_flags & LOAD_VCONFIG) &&
223 (load_flags & LOAD_SESSION) )
224 session->load_video_config(file, 0, load_flags);
226 result = file->skip_tag();
229 if( file->tag.title_is("AUDIO") ) {
230 if( (load_flags & LOAD_ACONFIG) &&
231 (load_flags & LOAD_SESSION) )
232 session->load_audio_config(file, 0, load_flags);
234 result = file->skip_tag();
237 if( file->tag.title_is("FOLDERS") ) {
238 result = folders.load_xml(file);
241 if( file->tag.title_is("MIXERS") ) {
242 if( (load_flags & LOAD_SESSION) )
245 result = file->skip_tag();
248 if( file->tag.title_is("ASSETS") ) {
249 if( load_flags & LOAD_ASSETS )
250 assets->load(file, load_flags);
252 result = file->skip_tag();
255 if( file->tag.title_is(labels->xml_tag) ) {
256 if( load_flags & LOAD_TIMEBAR )
257 labels->load(file, load_flags);
259 result = file->skip_tag();
262 if( file->tag.title_is("LOCALSESSION") ) {
263 if( (load_flags & LOAD_SESSION) ||
264 (load_flags & LOAD_TIMEBAR) )
265 local_session->load_xml(file, load_flags);
267 result = file->skip_tag();
270 if( file->tag.title_is("SESSION") ) {
271 if( (load_flags & LOAD_SESSION) &&
273 session->load_xml(file, 0, load_flags);
275 result = file->skip_tag();
278 if( file->tag.title_is("TRACK") ) {
279 tracks->load(file, track_offset, load_flags);
283 // Causes clip creation to fail because that involves an opening EDL tag.
284 if( file->tag.title_is("CLIP_EDL") && !parent_edl ) {
285 EDL *new_edl = new EDL(this);
286 new_edl->create_objects();
287 new_edl->read_xml(file, LOAD_ALL);
288 if( (load_flags & LOAD_ALL) == LOAD_ALL )
289 clips.add_clip(new_edl);
290 new_edl->remove_user();
293 if( file->tag.title_is("NESTED_EDL") ) {
294 EDL *nested_edl = new EDL;
295 nested_edl->create_objects();
296 nested_edl->read_xml(file, LOAD_ALL);
297 if( (load_flags & LOAD_ALL) == LOAD_ALL )
298 nested_edls.get_nested(nested_edl);
299 nested_edl->remove_user();
302 if( file->tag.title_is("VWINDOW_EDL") && !parent_edl ) {
303 EDL *new_edl = new EDL(this);
304 new_edl->create_objects();
305 new_edl->read_xml(file, LOAD_ALL);
308 if( (load_flags & LOAD_ALL) == LOAD_ALL ) {
309 // if( vwindow_edl && !vwindow_edl_shared )
310 // vwindow_edl->remove_user();
311 // vwindow_edl_shared = 0;
312 // vwindow_edl = new_edl;
314 append_vwindow_edl(new_edl, 0);
318 // Discard if not replacing EDL
320 new_edl->remove_user();
333 // Output path is the path of the output file if name truncation is desired.
334 // It is a "" if complete names should be used.
335 // Called recursively by copy for clips, thus the string can't be terminated.
336 // The string is not terminated in this call.
337 int EDL::save_xml(FileXML *file, const char *output_path)
339 copy(COPY_EDL, file, output_path, 0);
343 int EDL::copy_all(EDL *edl)
345 if( this == edl ) return 0;
346 folder_no = edl->folder_no;
353 tracks->copy_from(edl->tracks);
354 labels->copy_from(edl->labels);
358 void EDL::copy_clips(EDL *edl)
360 if( this == edl ) return;
362 remove_vwindow_edls();
364 // if( vwindow_edl && !vwindow_edl_shared )
365 // vwindow_edl->remove_user();
367 // vwindow_edl_shared = 0;
369 for( int i=0; i<edl->total_vwindow_edls(); ++i ) {
370 EDL *new_edl = new EDL(this);
371 new_edl->create_objects();
372 new_edl->copy_all(edl->get_vwindow_edl(i));
373 append_vwindow_edl(new_edl, 0);
377 for( int i=0; i<edl->clips.size(); ++i ) add_clip(edl->clips[i]);
380 void EDL::copy_nested(EDL *edl)
382 if( this == edl ) return;
383 nested_edls.copy_nested(edl->nested_edls);
386 void EDL::copy_assets(EDL *edl)
388 if( this == edl ) return;
391 assets->copy_from(edl->assets);
395 void EDL::copy_mixers(EDL *edl)
397 if( this == edl ) return;
398 mixers.copy_from(edl->mixers);
401 void EDL::copy_session(EDL *edl, int session_only)
403 if( this == edl ) return;
405 if( !session_only ) {
406 strcpy(this->path, edl->path);
407 folders.copy_from(&edl->folders);
411 session->copy(edl->session);
414 if( session_only <= 0 ) {
415 local_session->copy_from(edl->local_session);
419 int EDL::copy_assets(int copy_flags, double start, double end,
420 FileXML *file, const char *output_path)
422 ArrayList<Asset*> asset_list;
425 file->tag.set_title("ASSETS");
427 file->append_newline();
429 // Copy everything for a save
430 if( (copy_flags & COPY_ALL_ASSETS) ) {
431 for( Asset *asset=assets->first; asset; asset=asset->next ) {
432 asset_list.append(asset);
435 if( (copy_flags & COPY_USED_ASSETS) ) {
436 // Copy just the ones being used.
437 for( current = tracks->first; current; current = NEXT ) {
438 if( !current->record ) continue;
439 current->copy_assets(start, end, &asset_list);
443 // Paths relativised here
444 for( int i=0; i<asset_list.size(); ++i ) {
445 asset_list[i]->write(file, 0, output_path);
448 file->tag.set_title("/ASSETS");
450 file->append_newline();
451 file->append_newline();
456 int EDL::copy(int copy_flags, double start, double end,
457 FileXML *file, const char *output_path, int rewind_it)
459 file->tag.set_title("EDL");
460 file->tag.set_property("VERSION", CINELERRA_VERSION);
461 // Save path for restoration of the project title from a backup.
462 if( this->path[0] ) file->tag.set_property("PATH", path);
463 return copy_xml(copy_flags, start, end, file, "/EDL", output_path, rewind_it);
465 int EDL::copy(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
467 return copy(copy_flags, 0., tracks->total_length(),
468 file, output_path, rewind_it);
471 int EDL::copy_clip(int copy_flags, double start, double end,
472 FileXML *file, const char *output_path, int rewind_it)
474 file->tag.set_title("CLIP_EDL");
475 return copy_xml(copy_flags, start, end, file, "/CLIP_EDL", output_path, rewind_it);
477 int EDL::copy_clip(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
479 return copy_clip(copy_flags, 0., tracks->total_length(),
480 file, output_path, rewind_it);
483 int EDL::copy_nested(int copy_flags, double start, double end,
484 FileXML *file, const char *output_path, int rewind_it)
486 file->tag.set_title("NESTED_EDL");
487 if( this->path[0] ) file->tag.set_property("PATH", path);
488 return copy_xml(copy_flags, start, end, file, "/NESTED_EDL", output_path, rewind_it);
490 int EDL::copy_nested(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
492 return copy_nested(copy_flags, 0., tracks->total_length(),
493 file, output_path, rewind_it);
496 int EDL::copy_vwindow(int copy_flags, double start, double end,
497 FileXML *file, const char *output_path, int rewind_it)
499 file->tag.set_title("VWINDOW_EDL");
500 return copy_xml(copy_flags, start, end, file, "/VWINDOW_EDL", output_path, rewind_it);
502 int EDL::copy_vwindow(int copy_flags, FileXML *file, const char *output_path, int rewind_it)
504 return copy_vwindow(copy_flags, 0., tracks->total_length(),
505 file, output_path, rewind_it);
508 int EDL::copy_xml(int copy_flags, double start, double end,
509 FileXML *file, const char *closer, const char *output_path,
513 file->append_newline();
514 // Set clipboard samples only if copying to clipboard
515 if( (copy_flags & COPY_LENGTH) ) {
516 file->tag.set_title("CLIPBOARD");
517 file->tag.set_property("LENGTH", end - start);
519 file->tag.set_title("/CLIPBOARD");
521 file->append_newline();
522 file->append_newline();
524 //printf("EDL::copy 1\n");
527 if( (copy_flags & COPY_LOCAL_SESSION) )
528 local_session->save_xml(file, start);
531 // Need to copy all this from child EDL if pasting is desired.
533 if( (copy_flags & COPY_SESSION) )
534 session->save_xml(file);
536 if( (copy_flags & COPY_VIDEO_CONFIG) )
537 session->save_video_config(file);
539 if( (copy_flags & COPY_AUDIO_CONFIG) )
540 session->save_audio_config(file);
542 if( (copy_flags & COPY_FOLDERS) )
543 folders.save_xml(file);
545 if( (copy_flags & (COPY_ALL_ASSETS | COPY_USED_ASSETS)) )
546 copy_assets(copy_flags, start, end, file, output_path);
548 if( (copy_flags & COPY_NESTED_EDL) ) {
549 for( int i=0; i<nested_edls.size(); ++i )
550 nested_edls[i]->copy_nested(copy_flags,
551 file, output_path, 0);
554 if( (copy_flags & COPY_CLIPS) ) {
555 for( int i=0; i<clips.size(); ++i )
556 clips[i]->copy_clip(copy_flags, file, output_path, 0);
559 if( (copy_flags & COPY_VWINDOWS) ) {
560 for( int i=0; i<total_vwindow_edls(); ++i )
561 get_vwindow_edl(i)->copy_vwindow(copy_flags,
562 file, output_path, 0);
565 if( (copy_flags & COPY_MIXERS) )
568 file->append_newline();
569 file->append_newline();
571 if( (copy_flags & COPY_LABELS) )
572 labels->copy(start, end, file);
574 tracks->copy(copy_flags, start, end, file, output_path);
577 file->tag.set_title(closer);
579 file->append_newline();
581 // For editing operations we want to rewind it for immediate pasting.
582 // For clips and saving to disk leave it alone.
584 file->terminate_string();
590 void EDL::copy_indexables(EDL *edl)
592 for( Track *track=edl->tracks->first; track; track=track->next ) {
593 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
595 assets->update(edit->asset);
596 if( edit->nested_edl )
597 nested_edls.get_nested(edit->nested_edl);
602 EDL *EDL::new_nested(EDL *edl, const char *path)
604 EDL *nested = new EDL; // no parent for nested edl
605 nested->create_objects();
606 nested->copy_session(edl);
607 nested->set_path(path);
608 nested->update_index(edl);
609 nested->copy_indexables(edl);
610 nested->tracks->copy_from(edl->tracks);
611 nested_edls.append(nested);
615 EDL *EDL::create_nested_clip(EDL *nested)
617 EDL *new_edl = new EDL(this); // parent for clip edl
618 new_edl->create_objects();
619 new_edl->create_nested(nested);
623 void EDL::create_nested(EDL *nested)
625 // Keep frame rate, sample rate, and output size unchanged.
626 // Nest all video & audio outputs
627 session->video_tracks = 1;
628 session->audio_tracks = nested->session->audio_channels;
629 create_default_tracks();
630 insert_asset(0, nested, 0, 0, 0);
635 int min_w = session->output_w, min_h = session->output_h;
636 for( Track *track=tracks->first; track!=0; track=track->next ) {
637 if( track->data_type != TRACK_VIDEO ) continue;
638 int w = min_w, h = min_h;
639 for( Edit *current=track->edits->first; current!=0; current=NEXT ) {
640 Indexable* indexable = current->get_source();
641 if( !indexable ) continue;
642 int edit_w = indexable->get_w(), edit_h = indexable->get_h();
643 if( w < edit_w ) w = edit_w;
644 if( h < edit_h ) h = edit_h;
646 if( track->track_w == w && track->track_h == h ) continue;
647 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
648 translate_masks( (w - track->track_w) / 2, (h - track->track_h) / 2);
649 track->track_w = w; track->track_h = h;
653 void EDL::rechannel()
655 for( Track *current=tracks->first; current; current=NEXT ) {
656 if( current->data_type == TRACK_AUDIO ) {
657 PanAutos *autos = (PanAutos*)current->automation->autos[AUTOMATION_PAN];
658 ((PanAuto*)autos->default_auto)->rechannel();
659 for( PanAuto *keyframe = (PanAuto*)autos->first;
660 keyframe; keyframe = (PanAuto*)keyframe->next ) {
661 keyframe->rechannel();
667 void EDL::resample(double old_rate, double new_rate, int data_type)
669 for( Track *current=tracks->first; current; current=NEXT ) {
670 if( current->data_type == data_type ) {
671 current->resample(old_rate, new_rate);
677 void EDL::synchronize_params(EDL *edl)
679 local_session->synchronize_params(edl->local_session);
680 for( Track *this_track=tracks->first, *that_track=edl->tracks->first;
681 this_track && that_track;
682 this_track=this_track->next, that_track=that_track->next ) {
683 this_track->synchronize_params(that_track);
687 int EDL::trim_selection(double start,
701 tracks->total_length(),
710 int EDL::equivalent(double position1, double position2)
712 double threshold = session->cursor_on_frames ?
713 0.5 / session->frame_rate : 1.0 / session->sample_rate;
714 return fabs(position2 - position1) < threshold ? 1 : 0;
717 double EDL::equivalent_output(EDL *edl)
720 session->equivalent_output(edl->session, &result);
721 tracks->equivalent_output(edl->tracks, &result);
726 void EDL::set_path(const char *path)
728 if( &this->path[0] == path ) return;
729 strcpy(this->path, path);
732 void EDL::set_inpoint(double position)
734 if( equivalent(local_session->get_inpoint(), position) &&
735 local_session->get_inpoint() >= 0 ) {
736 local_session->unset_inpoint();
739 local_session->set_inpoint(align_to_frame(position, 0));
740 if( local_session->get_outpoint() <= local_session->get_inpoint() )
741 local_session->unset_outpoint();
745 void EDL::set_outpoint(double position)
747 if( equivalent(local_session->get_outpoint(), position) &&
748 local_session->get_outpoint() >= 0 ) {
749 local_session->unset_outpoint();
752 local_session->set_outpoint(align_to_frame(position, 0));
753 if( local_session->get_inpoint() >= local_session->get_outpoint() )
754 local_session->unset_inpoint();
758 void EDL::unset_inoutpoint()
760 local_session->unset_inpoint();
761 local_session->unset_outpoint();
764 int EDL::blade(double position)
766 return tracks->blade(position);
769 int EDL::clear(double start, double end,
770 int clear_labels, int clear_plugins, int edit_autos)
774 tracks->clear_handle(start,
780 if( clear_labels && distance > 0 )
781 labels->paste_silence(start,
795 // Need to put at beginning so a subsequent paste operation starts at the
797 double position = local_session->get_selectionstart();
798 local_session->set_selectionend(position);
799 local_session->set_selectionstart(position);
803 static int dead_edit_cmp(Edit**ap, Edit**bp)
805 Edit *a = *ap, *b = *bp;
806 if( a->track != b->track ) return 0;
807 return a->startproject > b->startproject ? -1 : 1;
810 void EDL::delete_edits(ArrayList<Edit*> *edits, int collapse)
812 edits->sort(dead_edit_cmp);
813 for( int i=0; i<edits->size(); ++i ) {
814 Edit *edit = edits->get(i);
815 Track *track = edit->track;
816 int64_t start = edit->startproject;
817 int64_t length = edit->length;
818 int64_t end = start + length;
819 if( session->autos_follow_edits ) {
820 track->automation->clear(start, end, 0, collapse);
822 if( session->plugins_follow_edits ) {
823 for( int k=0; k<track->plugin_set.size(); ++k ) {
824 PluginSet *plugin_set = track->plugin_set[k];
825 plugin_set->clear(start, end, 1);
827 plugin_set->paste_silence(start, end, 1);
828 plugin_set->optimize();
831 Edit *dead_edit = edit;
833 while( (edit=edit->next) )
834 edit->startproject -= length;
843 static int cmp(Range *ap, Range *bp);
845 bool operator ==(Range &that) { return this->start == that.start; }
846 bool operator >(Range &that) { return this->start > that.start; }
848 int Range::cmp(Range *ap, Range *bp) {
849 return ap->start < bp->start ? -1 : ap->start == bp->start ? 0 : 1;
852 static void get_edit_regions(ArrayList<Edit*> *edits, ArrayList<Range> ®ions)
854 // move edit inclusive labels by regions
855 for( int i=0; i<edits->size(); ++i ) {
856 Edit *edit = edits->get(i);
857 double pos = edit->track->from_units(edit->startproject);
858 double end = edit->track->from_units(edit->startproject + edit->length);
859 int n = regions.size(), k = n;
861 Range &range = regions[k];
862 if( pos >= range.end ) continue;
863 if( range.start >= end ) continue;
865 if( range.start > pos ) { range.start = pos; expand = 1; }
866 if( range.end < end ) { range.end = end; expand = 1; }
871 Range &range = regions.append();
872 range.start = pos; range.end = end;
875 regions.sort(Range::cmp);
878 void EDL::delete_edit_labels(ArrayList<Edit*> *edits, int collapse)
880 ArrayList<Range> regions;
881 get_edit_regions(edits, regions);
882 int n = regions.size(), k = n;
884 Range &range = regions[k];
885 labels->clear(range.start, range.end, collapse);
889 void EDL::move_edit_labels(ArrayList<Edit*> *edits, double dist)
891 ArrayList<Range> regions;
892 get_edit_regions(edits, regions);
893 int n = regions.size(), k = n;
894 Labels moved(this, 0);
896 Range &range = regions[k];
897 Label *label = labels->label_of(range.start);
898 for( Label *next=0; label && label->position <= range.end; label=next ) {
900 labels->remove_pointer(label);
901 label->position += dist;
904 Label *current = labels->first;
905 while( (label=moved.first) ) {
906 moved.remove_pointer(label);
907 while( current && current->position < label->position )
908 current = current->next;
909 if( current && current->position == label->position ) {
910 delete label; continue;
912 labels->insert_before(current, label);
917 void EDL::modify_edithandles(double oldposition, double newposition,
918 int currentend, int handle_mode, int edit_labels,
919 int edit_plugins, int edit_autos, int group_id)
921 tracks->modify_edithandles(oldposition, newposition,
922 currentend, handle_mode, edit_labels,
923 edit_plugins, edit_autos, group_id);
926 void EDL::modify_pluginhandles(double oldposition, double newposition,
927 int currentend, int handle_mode, int edit_labels,
928 int edit_autos, Edits *trim_edits)
930 tracks->modify_pluginhandles(oldposition, newposition,
931 currentend, handle_mode, edit_labels,
932 edit_autos, trim_edits);
936 void EDL::paste_silence(double start, double end,
937 int edit_labels, int edit_plugins, int edit_autos)
940 labels->paste_silence(start, end);
941 tracks->paste_silence(start, end, edit_plugins, edit_autos);
945 void EDL::remove_from_project(ArrayList<EDL*> *clips)
947 for( int i=0; i<clips->size(); ++i ) {
948 this->clips.remove_clip(clips->get(i));
952 void EDL::remove_from_project(ArrayList<Indexable*> *assets)
956 for( int j=0; j<clips.size(); ++j ) {
957 clips[j]->remove_from_project(assets);
960 // Remove from VWindow EDLs
961 for( int i=0; i<total_vwindow_edls(); ++i )
962 get_vwindow_edl(i)->remove_from_project(assets);
964 for( int i=0; i<assets->size(); ++i ) {
965 // Remove from tracks
966 for( Track *track=tracks->first; track; track=track->next ) {
967 track->remove_asset(assets->get(i));
970 // Remove from assets
971 if( !parent_edl && assets->get(i)->is_asset ) {
972 this->assets->remove_asset((Asset*)assets->get(i));
975 if( !parent_edl && !assets->get(i)->is_asset ) {
976 this->nested_edls.remove_clip((EDL*)assets->get(i));
981 void EDL::update_assets(EDL *src)
983 for( Asset *current=src->assets->first; current; current=NEXT ) {
984 assets->update(current);
988 int EDL::get_tracks_height(Theme *theme)
990 int total_pixels = 0;
991 for( Track *current=tracks->first; current; current=NEXT ) {
992 total_pixels += current->vertical_span(theme);
997 int64_t EDL::get_tracks_width()
999 int64_t total_pixels = 0;
1000 for( Track *current=tracks->first; current; current=NEXT ) {
1001 int64_t pixels = current->horizontal_span();
1002 if( pixels > total_pixels ) total_pixels = pixels;
1004 //printf("EDL::get_tracks_width %d\n", total_pixels);
1005 return total_pixels;
1008 // int EDL::calculate_output_w(int single_channel)
1010 // if( single_channel ) return session->output_w;
1013 // for( int i=0; i<session->video_channels; ++i )
1015 // if( session->vchannel_x[i] + session->output_w > widest ) widest = session->vchannel_x[i] + session->output_w;
1020 // int EDL::calculate_output_h(int single_channel)
1022 // if( single_channel ) return session->output_h;
1025 // for( int i=0; i<session->video_channels; ++i )
1027 // if( session->vchannel_y[i] + session->output_h > tallest ) tallest = session->vchannel_y[i] + session->output_h;
1032 // Get the total output size scaled to aspect ratio
1033 void EDL::calculate_conformed_dimensions(int single_channel, float &w, float &h)
1035 if( (float)session->output_w / session->output_h > get_aspect_ratio() )
1036 h = (w = session->output_w) / get_aspect_ratio();
1038 w = (h = session->output_h) * get_aspect_ratio();
1041 float EDL::get_aspect_ratio()
1043 return session->aspect_w / session->aspect_h;
1046 int EDL::dump(FILE *fp)
1049 fprintf(fp,"CLIP\n");
1051 fprintf(fp,"EDL\n");
1052 fprintf(fp," clip_title: %s\n"
1053 " parent_edl: %p\n", local_session->clip_title, parent_edl);
1054 fprintf(fp," selectionstart %f\n selectionend %f\n loop_start %f\n loop_end %f\n",
1055 local_session->get_selectionstart(1),
1056 local_session->get_selectionend(1),
1057 local_session->loop_start,
1058 local_session->loop_end);
1059 for( int i=0; i<TOTAL_PANES; ++i ) {
1060 fprintf(fp," pane %d view_start=%jd track_start=%d\n", i,
1061 local_session->view_start[i],
1062 local_session->track_start[i]);
1066 fprintf(fp,"audio_channels: %d audio_tracks: %d sample_rate: %jd\n",
1067 session->audio_channels,
1068 session->audio_tracks,
1069 session->sample_rate);
1070 fprintf(fp," video_channels: %d\n"
1071 " video_tracks: %d\n"
1072 " frame_rate: %.2f\n"
1073 " frames_per_foot: %.2f\n"
1078 " color_model: %d\n",
1079 session->video_channels,
1080 session->video_tracks,
1081 session->frame_rate,
1082 session->frames_per_foot,
1087 session->color_model);
1089 fprintf(fp," CLIPS");
1090 fprintf(fp," total: %d\n", clips.size());
1091 for( int i=0; i<clips.size(); ++i ) {
1096 fprintf(fp," NESTED_EDLS");
1097 fprintf(fp," total: %d\n", nested_edls.size());
1098 for( int i=0; i<nested_edls.size(); ++i )
1099 fprintf(fp," %s\n", nested_edls[i]->path);
1101 fprintf(fp," VWINDOW EDLS");
1102 fprintf(fp," total: %d\n", total_vwindow_edls());
1104 for( int i=0; i<total_vwindow_edls(); ++i ) {
1105 fprintf(fp," %s\n", get_vwindow_edl(i)->local_session->clip_title);
1108 fprintf(fp," ASSETS\n");
1111 fprintf(fp," LABELS\n");
1113 fprintf(fp," TRACKS\n");
1115 //printf("EDL::dump 2\n");
1119 EDL* EDL::add_clip(EDL *edl)
1121 // Copy argument. New edls are deleted from MWindow::load_filenames.
1122 EDL *new_edl = new EDL(this);
1123 new_edl->create_objects();
1124 new_edl->copy_all(edl);
1125 new_edl->folder_no = AW_CLIP_FOLDER;
1126 clips.append(new_edl);
1130 void EDL::insert_asset(Asset *asset,
1134 RecordLabels *labels)
1136 // Insert asset into asset table
1137 Asset *new_asset = 0;
1138 EDL *new_nested_edl = 0;
1140 if( asset ) new_asset = assets->update(asset);
1141 if( nested_edl ) new_nested_edl = nested_edls.get_nested(nested_edl);
1145 Track *current = first_track ? first_track : tracks->first;
1148 // Fix length of single frame
1153 if( new_nested_edl ) {
1154 length = new_nested_edl->tracks->total_length();
1156 channels = new_nested_edl->session->audio_channels;
1160 // Insert 1 frame for undefined length
1161 if( new_asset->video_length < 0 ) {
1162 length = session->si_useduration ?
1163 session->si_duration :
1164 1.0 / session->frame_rate;
1167 length = new_asset->frame_rate > 0 ?
1168 (double)new_asset->video_length / new_asset->frame_rate :
1169 1.0 / session->frame_rate;
1171 layers = new_asset->layers;
1172 channels = new_asset->channels;
1175 for( ; current && vtrack<layers; current=NEXT ) {
1176 if( !current->record || current->data_type != TRACK_VIDEO ) continue;
1177 current->insert_asset(new_asset, new_nested_edl,
1178 length, position, vtrack++);
1183 if( new_asset->audio_length < 0 ) {
1184 // Insert 1 frame for undefined length & video
1185 if( new_asset->video_data )
1186 length = (double)1.0 / new_asset->frame_rate;
1188 // Insert 1 second for undefined length & no video
1192 length = (double)new_asset->audio_length /
1193 new_asset->sample_rate;
1196 current = tracks->first;
1197 for( ; current && atrack < channels; current=NEXT ) {
1198 if( !current->record || current->data_type != TRACK_AUDIO ) continue;
1199 current->insert_asset(new_asset, new_nested_edl,
1200 length, position, atrack++);
1203 // Insert labels from a recording window.
1205 for( RecordLabel *label=labels->first; label; label=label->next ) {
1206 this->labels->toggle_label(label->position, label->position);
1213 void EDL::set_index_file(Indexable *indexable)
1215 if( indexable->is_asset )
1216 assets->update_index((Asset*)indexable);
1218 nested_edls.update_index((EDL*)indexable);
1221 void EDL::optimize()
1223 //printf("EDL::optimize 1\n");
1224 double length = tracks->total_length();
1225 double preview_start = local_session->preview_start;
1226 double preview_end = local_session->preview_end;
1227 if( preview_end < 0 || preview_end > length )
1228 preview_end = length;
1229 if( preview_start == 0 && preview_end >= length )
1230 local_session->preview_end = -1;
1231 if( preview_start > preview_end )
1232 local_session->preview_start = preview_end;
1233 for( Track *current=tracks->first; current; current=NEXT )
1234 current->optimize();
1239 static Mutex id_lock;
1240 id_lock.lock("EDL::next_id");
1241 int result = EDLSession::current_id++;
1246 void EDL::get_shared_plugins(Track *source,
1247 ArrayList<SharedLocation*> *plugin_locations,
1248 int omit_recordable,
1251 for( Track *track=tracks->first; track; track=track->next ) {
1252 if( track->record && omit_recordable ) continue;
1253 if( track == source || track->data_type != data_type ) continue;
1254 for( int i=0; i<track->plugin_set.size(); ++i ) {
1255 Plugin *plugin = track->get_current_plugin(
1256 local_session->get_selectionstart(1),
1257 i, PLAY_FORWARD, 1, 0);
1258 if( plugin && plugin->plugin_type != PLUGIN_STANDALONE ) continue;
1259 plugin_locations->append(new SharedLocation(tracks->number_of(track), i));
1264 void EDL::get_shared_tracks(Track *track,
1265 ArrayList<SharedLocation*> *module_locations,
1266 int omit_recordable, int data_type)
1268 for( Track *current=tracks->first; current; current=NEXT ) {
1269 if( omit_recordable && current->record ) continue;
1270 if( current == track || current->data_type != data_type ) continue;
1271 module_locations->append(new SharedLocation(tracks->number_of(current), 0));
1275 // aligned frame time, account for sample truncation
1276 double EDL::frame_align(double position, int round)
1278 if( !round && session->sample_rate > 0 ) {
1279 int64_t sample_pos = position * session->sample_rate;
1280 position = (sample_pos+2.) / session->sample_rate;
1282 int64_t frame_pos = (position * session->frame_rate + (round ? 0.5 : 1e-6));
1283 return frame_pos / session->frame_rate;
1286 // Convert position to frames if alignment is enabled.
1287 double EDL::align_to_frame(double position, int round)
1289 if( session->cursor_on_frames )
1290 position = frame_align(position, round);
1295 BinFolder *EDL::get_folder(int no)
1297 for( int i=0; i<folders.size(); ++i ) {
1298 BinFolder *fp = folders[i];
1299 if( no == fp->awindow_folder ) return fp;
1304 int EDL::get_folder_number(const char *title)
1306 for( int i=0; i<AWINDOW_FOLDERS; ++i ) {
1307 if( !strcmp(title, AWindowGUI::folder_names[i]) )
1310 for( int i=0; i<folders.size(); ++i ) {
1311 if( !strcmp(title, folders[i]->title) )
1312 return folders[i]->awindow_folder;
1314 return AW_NO_FOLDER;
1317 const char *EDL::get_folder_name(int no)
1319 if( no >= 0 && no<AWINDOW_FOLDERS )
1320 return AWindowGUI::folder_names[no];
1321 BinFolder *fp = get_folder(no);
1322 return !fp ? "" : fp->title;
1325 int EDL::new_folder(const char *title, int is_clips)
1327 if( !title[0] ) return 1;
1328 int ret = get_folder_number(title);
1329 if( ret >= 0 ) return 1;
1330 int idx = AWINDOW_FOLDERS;
1331 for( int i=0; i<folders.size(); ++i ) {
1332 BinFolder *fp = folders[i];
1333 int no = fp->awindow_folder;
1334 if( no >= idx ) idx = no+1;
1336 folders.append(new BinFolder(idx, is_clips, title));
1340 int EDL::delete_folder(const char *title)
1342 int k = folders.size();
1343 while( --k >= 0 && strcmp(title, folders[k]->title) );
1345 folders.remove_object_number(k);
1349 int EDL::get_use_vconsole(VEdit* *playable_edit,
1350 int64_t position, int direction, PlayableTracks *playable_tracks)
1352 int share_playable_tracks = 1;
1354 VTrack *playable_track = 0;
1355 const int debug = 0;
1358 // Calculate playable tracks when being called as a nested EDL
1359 if( !playable_tracks ) {
1360 share_playable_tracks = 0;
1361 playable_tracks = new PlayableTracks(this,
1362 position, direction, TRACK_VIDEO, 1);
1366 // Total number of playable tracks is 1
1367 if( playable_tracks->size() != 1 ) {
1371 playable_track = (VTrack*)playable_tracks->get(0);
1374 // Don't need playable tracks anymore
1375 if( !share_playable_tracks ) {
1376 delete playable_tracks;
1379 if( debug ) printf("EDL::get_use_vconsole %d playable_tracks->size()=%d\n",
1380 __LINE__, playable_tracks->size());
1381 if( result ) return 1;
1384 // Test mutual conditions between direct copy rendering and this.
1385 if( !playable_track->direct_copy_possible(position,
1389 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1391 *playable_edit = (VEdit*)playable_track->edits->editof(position,
1393 // No edit at current location
1394 if( !*playable_edit ) return 1;
1395 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1398 // Edit is nested EDL
1399 if( (*playable_edit)->nested_edl ) {
1401 EDL *nested_edl = (*playable_edit)->nested_edl;
1402 int64_t nested_position = (int64_t)((position -
1403 (*playable_edit)->startproject +
1404 (*playable_edit)->startsource) *
1405 nested_edl->session->frame_rate /
1406 session->frame_rate);
1409 VEdit *playable_edit_temp = 0;
1410 if( session->output_w != nested_edl->session->output_w ||
1411 session->output_h != nested_edl->session->output_h ||
1412 nested_edl->get_use_vconsole(&playable_edit_temp,
1421 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1422 // Edit is not a nested EDL
1423 Asset *asset = (*playable_edit)->asset;
1425 if( !asset ) return 1;
1426 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1428 // Asset and output device must have the same dimensions
1429 if( asset->width != session->output_w ||
1430 asset->height != session->output_h )
1434 if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__);
1435 // Asset and output device must have same resulting de-interlacing method
1436 if( ilaceautofixmethod2(session->interlace_mode,
1437 asset->interlace_autofixoption, asset->interlace_mode,
1438 asset->interlace_fixmethod) != ILACE_FIXMETHOD_NONE )
1441 // If we get here the frame is going to be directly copied. Whether it is
1442 // decompressed in hardware depends on the colormodel.
1448 int EDL::get_audio_channels()
1450 return session->audio_channels;
1453 int EDL::get_sample_rate()
1455 return session->sample_rate;
1458 int64_t EDL::get_audio_samples()
1460 return (int64_t)(tracks->total_length() *
1461 session->sample_rate);
1464 int EDL::have_audio()
1469 int EDL::have_video()
1477 return session->output_w;
1482 return session->output_h;
1485 double EDL::get_frame_rate()
1487 return session->frame_rate;
1490 int EDL::get_video_layers()
1495 int64_t EDL::get_video_frames()
1497 return (int64_t)(tracks->total_length() *
1498 session->frame_rate);
1501 void EDL::remove_vwindow_edls()
1503 for( int i=0; i<total_vwindow_edls(); ++i ) {
1504 get_vwindow_edl(i)->remove_user();
1506 vwindow_edls.remove_all();
1509 void EDL::remove_vwindow_edl(EDL *edl)
1511 if( vwindow_edls.number_of(edl) >= 0 ) {
1513 vwindow_edls.remove(edl);
1518 EDL* EDL::get_vwindow_edl(int number)
1520 return vwindow_edls.get(number);
1523 int EDL::total_vwindow_edls()
1525 return vwindow_edls.size();
1528 void EDL::append_vwindow_edl(EDL *edl, int increase_counter)
1530 if(vwindow_edls.number_of(edl) >= 0) return;
1532 if(increase_counter) edl->add_user();
1533 vwindow_edls.append(edl);
1537 double EDL::next_edit(double position)
1539 Units::fix_double(&position);
1540 double new_position = tracks->total_length();
1542 double max_rate = get_frame_rate();
1543 int sample_rate = get_sample_rate();
1544 if( sample_rate > max_rate ) max_rate = sample_rate;
1545 double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
1547 // Test for edit handles after position
1548 for( Track *track=tracks->first; track; track=track->next ) {
1549 if( !track->record ) continue;
1550 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1551 double edit_end = track->from_units(edit->startproject + edit->length);
1552 Units::fix_double(&edit_end);
1553 if( fabs(edit_end-position) < min_movement ) continue;
1554 if( edit_end > position && edit_end < new_position )
1555 new_position = edit_end;
1558 return new_position;
1561 double EDL::prev_edit(double position)
1563 Units::fix_double(&position);
1564 double new_position = -1;
1566 double max_rate = get_frame_rate();
1567 int sample_rate = get_sample_rate();
1568 if( sample_rate > max_rate ) max_rate = sample_rate;
1569 double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6;
1571 // Test for edit handles before cursor position
1572 for( Track *track=tracks->first; track; track=track->next ) {
1573 if( !track->record ) continue;
1574 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1575 double edit_end = track->from_units(edit->startproject);
1576 Units::fix_double(&edit_end);
1577 if( fabs(edit_end-position) < min_movement ) continue;
1578 if( edit_end < position && edit_end > new_position )
1579 new_position = edit_end;
1582 return new_position;
1585 void EDL::rescale_proxy(int orig_scale, int new_scale)
1587 if( orig_scale == new_scale ) return;
1589 float orig_w = (float)session->output_w * orig_scale;
1590 float orig_h = (float)session->output_h * orig_scale;
1592 session->output_w = Units::round(orig_w / new_scale);
1593 session->output_h = Units::round(orig_h / new_scale);
1597 for( Track *track=tracks->first; track; track=track->next ) {
1598 if( track->data_type != TRACK_VIDEO ) continue;
1599 orig_w = (float)track->track_w * orig_scale;
1600 orig_h = (float)track->track_h * orig_scale;
1601 track->track_w = Units::round(orig_w / new_scale);
1602 track->track_h = Units::round(orig_h / new_scale);
1603 ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])->
1604 set_proxy(orig_scale, new_scale);
1605 ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_X])->
1606 set_proxy(orig_scale, new_scale);
1607 ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_Y])->
1608 set_proxy(orig_scale, new_scale);
1609 ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_X])->
1610 set_proxy(orig_scale, new_scale);
1611 ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_Y])->
1612 set_proxy(orig_scale, new_scale);
1616 void EDL::set_proxy(int new_scale, int use_scaler,
1617 ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
1619 int orig_scale = session->proxy_scale;
1620 int orig_use_scaler = session->proxy_use_scaler;
1622 // rescale to full size asset in read_frame
1623 session->proxy_scale = new_scale;
1624 session->proxy_use_scaler = use_scaler;
1627 for( int i=0; i<proxy_assets->size(); ++i ) {
1628 Asset *proxy_asset = (Asset *)proxy_assets->get(i);
1629 proxy_asset->width = orig_assets->get(i)->get_w();
1630 proxy_asset->height = orig_assets->get(i)->get_h();
1635 if( !orig_use_scaler )
1636 rescale_proxy(orig_scale, new_scale);
1638 // change original assets to proxy assets
1639 int folder_no = use_scaler || new_scale != 1 ? AW_PROXY_FOLDER : AW_MEDIA_FOLDER;
1640 for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1641 Indexable *proxy_idxbl = proxy_assets->get(i);
1642 proxy_idxbl->folder_no = folder_no;
1643 if( !proxy_idxbl->is_asset ) continue;
1644 Asset *proxy_asset = assets->update((Asset *)proxy_idxbl);
1645 if( proxy_asset == (Asset *)proxy_idxbl ) continue;
1646 proxy_asset->width = proxy_idxbl->get_w();
1647 proxy_asset->height = proxy_idxbl->get_h();
1649 // replace track contents
1650 for( Track *track=tracks->first; track; track=track->next ) {
1651 if( track->data_type != TRACK_VIDEO ) continue;
1652 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1653 Indexable *idxbl = (Indexable *)edit->asset;
1654 if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1655 if( !idxbl ) continue;
1656 int i = orig_assets->size();
1657 while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) );
1658 if( i < 0 ) continue;
1659 Indexable *proxy_idxbl = proxy_assets->get(i);
1660 Asset *proxy_asset = proxy_idxbl->is_asset ?
1661 assets->update((Asset *)proxy_idxbl) : 0;
1662 EDL *proxy_edl = !proxy_idxbl->is_asset ?
1663 (EDL *)proxy_idxbl : 0;
1664 edit->asset = proxy_asset;
1665 edit->nested_edl = proxy_edl;
1668 for( int j=0,n=clips.size(); j<n; ++j ) {
1669 EDL *clip = clips[j];
1671 for( Track *track=clip->tracks->first; track; track=track->next ) {
1672 if( track->data_type != TRACK_VIDEO ) continue;
1673 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1674 Indexable *idxbl = (Indexable *)edit->asset;
1675 if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1676 if( !idxbl ) continue;
1677 int i = orig_assets->size();
1678 while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) );
1679 if( i < 0 ) continue;
1680 Indexable *proxy_idxbl = proxy_assets->get(i);
1681 Asset *proxy_asset = proxy_idxbl->is_asset ?
1682 assets->update((Asset *)proxy_idxbl) : 0;
1683 EDL *proxy_edl = !proxy_idxbl->is_asset ?
1684 (EDL *)proxy_idxbl : 0;
1685 edit->asset = proxy_asset;
1686 edit->nested_edl = proxy_edl;
1690 if( has_proxy && !orig_use_scaler )
1691 clip->rescale_proxy(orig_scale, new_scale);
1695 void EDL::add_proxy(int use_scaler,
1696 ArrayList<Indexable*> *orig_assets, ArrayList<Indexable*> *proxy_assets)
1699 for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1700 Asset *proxy_asset = (Asset *)proxy_assets->get(i);
1701 proxy_asset->width = orig_assets->get(i)->get_w();
1702 proxy_asset->height = orig_assets->get(i)->get_h();
1706 // change original assets to proxy assets
1707 for( int i=0,n=proxy_assets->size(); i<n; ++i ) {
1708 Asset *proxy_asset = assets->update((Asset *)proxy_assets->get(i));
1709 proxy_asset->folder_no = AW_PROXY_FOLDER;
1710 // replace track contents
1711 for( Track *track=tracks->first; track; track=track->next ) {
1712 if( track->data_type != TRACK_VIDEO ) continue;
1713 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1714 if( !edit->asset ) continue;
1715 if( !strcmp(edit->asset->path, orig_assets->get(i)->path) ) {
1716 edit->asset = proxy_asset;
1723 Asset *EDL::get_proxy_asset()
1725 return folder_no == AW_PROXY_FOLDER ?
1726 tracks->first->edits->first->asset : 0;
1729 Track *EDL::add_new_track(int data_type)
1731 Track *new_track = 0;
1732 switch( data_type ) {
1734 ++session->video_tracks;
1735 new_track = tracks->add_video_track(0, 0);
1738 ++session->audio_tracks;
1739 new_track = tracks->add_audio_track(0, 0);
1741 case TRACK_SUBTITLE:
1742 new_track = tracks->add_subttl_track(0, 0);
1748 double EDL::get_cursor_position(int cursor_x, int pane_no)
1750 return (double)cursor_x * local_session->zoom_sample / session->sample_rate +
1751 (double)local_session->view_start[pane_no] *
1752 local_session->zoom_sample / session->sample_rate;
1754 int64_t EDL::get_position_cursorx(double position, int pane_no)
1756 return (int64_t)(position * session->sample_rate / local_session->zoom_sample)
1757 - local_session->view_start[pane_no];
1760 int EDL::in_use(Indexable *indexable)
1762 for( Track *track=tracks->first; track; track=track->next ) {
1763 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1764 Indexable *idxbl = (Indexable *)edit->asset;
1765 if( !idxbl ) idxbl = (Indexable *)edit->nested_edl;
1766 if( !idxbl ) continue;
1767 if( idxbl->id == indexable->id ) return 1;
1768 if( !indexable->is_asset != !idxbl->is_asset ) continue;
1769 if( !strcmp(idxbl->path, indexable->path) ) return 1;
1772 for( int i=0; i<clips.size(); ++i )
1773 if( clips[i]->in_use(indexable) ) return 1;
1774 for( int i=0; i<nested_edls.size(); ++i )
1775 if( nested_edls[i]->in_use(indexable) ) return 1;
1779 int EDL::regroup(int next_id)
1781 ArrayList<int> new_groups;
1782 for( Track *track=tracks->first; track; track=track->next ) {
1783 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1784 if( !edit->group_id ) continue;
1785 int k = new_groups.size();
1786 while( --k >= 0 && new_groups[k] != edit->group_id );
1787 if( k >= 0 ) continue;
1788 new_groups.append(edit->group_id);
1791 for( Track *track=tracks->first; track; track=track->next ) {
1792 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
1793 if( !edit->group_id ) continue;
1794 int k = new_groups.size();
1795 while( --k >= 0 && new_groups[k] != edit->group_id );
1796 if( k < 0 ) continue;
1797 edit->group_id = k + next_id;
1800 return new_groups.size();