/* * CINELERRA * Copyright (C) 1997-2012 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "asset.h" #include "assets.h" #include "atrack.h" #include "autoconf.h" #include "automation.h" #include "awindowgui.h" #include "bccmodels.h" #include "bchash.h" #include "bcsignals.h" #include "clip.h" #include "cstrdup.h" #include "clipedls.h" #include "edits.h" #include "edl.h" #include "edlsession.h" #include "file.h" #include "filexml.h" #include "floatauto.h" #include "floatautos.h" #include "guicast.h" #include "keyframe.h" #include "keyframes.h" #include "indexstate.h" #include "labels.h" #include "localsession.h" #include "maskautos.h" #include "mutex.h" #include "panauto.h" #include "panautos.h" #include "playbackconfig.h" #include "playabletracks.h" #include "plugin.h" #include "pluginset.h" #include "preferences.h" #include "recordconfig.h" #include "recordlabel.h" #include "sharedlocation.h" #include "theme.h" #include "tracks.h" #include "transportque.inc" #include "versioninfo.h" #include "vedit.h" #include "vtrack.h" EDL::EDL(EDL *parent_edl) : Indexable(0) { this->parent_edl = parent_edl; tracks = 0; labels = 0; local_session = 0; id = next_id(); path[0] = 0; } EDL::~EDL() { delete tracks; delete labels; delete local_session; remove_vwindow_edls(); if( !parent_edl ) { delete assets; delete session; } } void EDL::create_objects() { tracks = new Tracks(this); assets = !parent_edl ? new Assets(this) : parent_edl->assets; session = !parent_edl ? new EDLSession(this) : parent_edl->session; local_session = new LocalSession(this); labels = new Labels(this, "LABELS"); } EDL& EDL::operator=(EDL &edl) { printf("EDL::operator= 1\n"); copy_all(&edl); return *this; } int EDL::load_defaults(BC_Hash *defaults) { if( !parent_edl ) session->load_defaults(defaults); local_session->load_defaults(defaults); return 0; } int EDL::save_defaults(BC_Hash *defaults) { if( !parent_edl ) session->save_defaults(defaults); local_session->save_defaults(defaults); return 0; } void EDL::boundaries() { session->boundaries(); local_session->boundaries(); } int EDL::create_default_tracks() { for( int i=0; ivideo_tracks; ++i ) { tracks->add_video_track(0, 0); } for( int i=0; iaudio_tracks; ++i ) { tracks->add_audio_track(0, 0); } return 0; } int EDL::load_xml(FileXML *file, uint32_t load_flags) { int result = 0; folders.clear(); if( (load_flags & LOAD_ALL) == LOAD_ALL ) { remove_vwindow_edls(); } // Search for start of master EDL. // The parent_edl test caused clip creation to fail since those XML files // contained an EDL tag. // The parent_edl test is required to make EDL loading work because // when loading an EDL the EDL tag is already read by the parent. if( !parent_edl ) { do { result = file->read_tag(); } while(!result && !file->tag.title_is("XML") && !file->tag.title_is("EDL")); } return result ? result : read_xml(file, load_flags); } int EDL::read_xml(FileXML *file, uint32_t load_flags) { int result = 0; // Track numbering offset for replacing undo data. int track_offset = 0; // Get path for backups file->tag.get_property("path", path); // Erase everything if( (load_flags & LOAD_ALL) == LOAD_ALL || (load_flags & LOAD_EDITS) == LOAD_EDITS ) { while(tracks->last) delete tracks->last; } if( (load_flags & LOAD_ALL) == LOAD_ALL ) { clips.clear(); mixers.remove_all_objects(); } if( load_flags & LOAD_TIMEBAR ) { while(labels->last) delete labels->last; local_session->unset_inpoint(); local_session->unset_outpoint(); } // This was originally in LocalSession::load_xml if( load_flags & LOAD_SESSION ) { local_session->clipboard_length = 0; } do { result = file->read_tag(); if( !result ) { if( file->tag.title_is("/XML") || file->tag.title_is("/EDL") || file->tag.title_is("/CLIP_EDL") || file->tag.title_is("/NESTED_EDL") || file->tag.title_is("/VWINDOW_EDL") ) { break; } else if( file->tag.title_is("CLIPBOARD") ) { local_session->clipboard_length = file->tag.get_property("LENGTH", (double)0); } else if( file->tag.title_is("VIDEO") ) { if( (load_flags & LOAD_VCONFIG) && (load_flags & LOAD_SESSION) ) session->load_video_config(file, 0, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is("AUDIO") ) { if( (load_flags & LOAD_ACONFIG) && (load_flags & LOAD_SESSION) ) session->load_audio_config(file, 0, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is("FOLDERS") ) { result = folders.load_xml(file); } else if( file->tag.title_is("MIXERS") ) { if( (load_flags & LOAD_SESSION) ) mixers.load(file); else result = file->skip_tag(); } else if( file->tag.title_is("ASSETS") ) { if( load_flags & LOAD_ASSETS ) assets->load(file, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is(labels->xml_tag) ) { if( load_flags & LOAD_TIMEBAR ) labels->load(file, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is("LOCALSESSION") ) { if( (load_flags & LOAD_SESSION) || (load_flags & LOAD_TIMEBAR) ) local_session->load_xml(file, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is("SESSION") ) { if( (load_flags & LOAD_SESSION) && !parent_edl ) session->load_xml(file, 0, load_flags); else result = file->skip_tag(); } else if( file->tag.title_is("TRACK") ) { tracks->load(file, track_offset, load_flags); } else // Sub EDL. // Causes clip creation to fail because that involves an opening EDL tag. if( file->tag.title_is("CLIP_EDL") && !parent_edl ) { EDL *new_edl = new EDL(this); new_edl->create_objects(); new_edl->read_xml(file, LOAD_ALL); if( (load_flags & LOAD_ALL) == LOAD_ALL ) clips.add_clip(new_edl); new_edl->remove_user(); } else if( file->tag.title_is("NESTED_EDL") ) { EDL *nested_edl = new EDL; nested_edl->create_objects(); nested_edl->read_xml(file, LOAD_ALL); if( (load_flags & LOAD_ALL) == LOAD_ALL ) nested_edls.get_nested(nested_edl); nested_edl->remove_user(); } else if( file->tag.title_is("VWINDOW_EDL") && !parent_edl ) { EDL *new_edl = new EDL(this); new_edl->create_objects(); new_edl->read_xml(file, LOAD_ALL); if( (load_flags & LOAD_ALL) == LOAD_ALL ) { // if( vwindow_edl && !vwindow_edl_shared ) // vwindow_edl->remove_user(); // vwindow_edl_shared = 0; // vwindow_edl = new_edl; append_vwindow_edl(new_edl, 0); } else // Discard if not replacing EDL { new_edl->remove_user(); new_edl = 0; } } } } while(!result); boundaries(); //dump(); return 0; } // Output path is the path of the output file if name truncation is desired. // It is a "" if complete names should be used. // Called recursively by copy for clips, thus the string can't be terminated. // The string is not terminated in this call. int EDL::save_xml(FileXML *file, const char *output_path) { copy(COPY_EDL, file, output_path, 0); return 0; } int EDL::copy_all(EDL *edl) { if( this == edl ) return 0; folder_no = edl->folder_no; update_index(edl); copy_session(edl); copy_assets(edl); copy_clips(edl); copy_nested(edl); copy_mixers(edl); tracks->copy_from(edl->tracks); labels->copy_from(edl->labels); return 0; } void EDL::copy_clips(EDL *edl) { if( this == edl ) return; remove_vwindow_edls(); // if( vwindow_edl && !vwindow_edl_shared ) // vwindow_edl->remove_user(); // vwindow_edl = 0; // vwindow_edl_shared = 0; for( int i=0; itotal_vwindow_edls(); ++i ) { EDL *new_edl = new EDL(this); new_edl->create_objects(); new_edl->copy_all(edl->get_vwindow_edl(i)); append_vwindow_edl(new_edl, 0); } clips.clear(); for( int i=0; iclips.size(); ++i ) add_clip(edl->clips[i]); } void EDL::copy_nested(EDL *edl) { if( this == edl ) return; nested_edls.copy_nested(edl->nested_edls); } void EDL::copy_assets(EDL *edl) { if( this == edl ) return; if( !parent_edl ) { assets->copy_from(edl->assets); } } void EDL::copy_mixers(EDL *edl) { if( this == edl ) return; mixers.copy_from(edl->mixers); } void EDL::copy_session(EDL *edl, int session_only) { if( this == edl ) return; if( !session_only ) { strcpy(this->path, edl->path); folders.copy_from(&edl->folders); } if( !parent_edl ) { session->copy(edl->session); } if( session_only <= 0 ) { local_session->copy_from(edl->local_session); } } int EDL::copy_assets(int copy_flags, double start, double end, FileXML *file, const char *output_path) { ArrayList asset_list; Track* current; file->tag.set_title("ASSETS"); file->append_tag(); file->append_newline(); // Copy everything for a save if( (copy_flags & COPY_ALL_ASSETS) ) { for( Asset *asset=assets->first; asset; asset=asset->next ) { asset_list.append(asset); } } if( (copy_flags & COPY_USED_ASSETS) ) { // Copy just the ones being used. for( current = tracks->first; current; current = NEXT ) { if( !current->record ) continue; current->copy_assets(start, end, &asset_list); } } // Paths relativised here for( int i=0; iwrite(file, 0, output_path); } file->tag.set_title("/ASSETS"); file->append_tag(); file->append_newline(); file->append_newline(); return 0; } int EDL::copy(int copy_flags, double start, double end, FileXML *file, const char *output_path, int rewind_it) { file->tag.set_title("EDL"); file->tag.set_property("VERSION", CINELERRA_VERSION); // Save path for restoration of the project title from a backup. if( this->path[0] ) file->tag.set_property("PATH", path); return copy_xml(copy_flags, start, end, file, "/EDL", output_path, rewind_it); } int EDL::copy(int copy_flags, FileXML *file, const char *output_path, int rewind_it) { return copy(copy_flags, 0., tracks->total_length(), file, output_path, rewind_it); } int EDL::copy_clip(int copy_flags, double start, double end, FileXML *file, const char *output_path, int rewind_it) { file->tag.set_title("CLIP_EDL"); return copy_xml(copy_flags, start, end, file, "/CLIP_EDL", output_path, rewind_it); } int EDL::copy_clip(int copy_flags, FileXML *file, const char *output_path, int rewind_it) { return copy_clip(copy_flags, 0., tracks->total_length(), file, output_path, rewind_it); } int EDL::copy_nested(int copy_flags, double start, double end, FileXML *file, const char *output_path, int rewind_it) { file->tag.set_title("NESTED_EDL"); if( this->path[0] ) file->tag.set_property("PATH", path); return copy_xml(copy_flags, start, end, file, "/NESTED_EDL", output_path, rewind_it); } int EDL::copy_nested(int copy_flags, FileXML *file, const char *output_path, int rewind_it) { return copy_nested(copy_flags, 0., tracks->total_length(), file, output_path, rewind_it); } int EDL::copy_vwindow(int copy_flags, double start, double end, FileXML *file, const char *output_path, int rewind_it) { file->tag.set_title("VWINDOW_EDL"); return copy_xml(copy_flags, start, end, file, "/VWINDOW_EDL", output_path, rewind_it); } int EDL::copy_vwindow(int copy_flags, FileXML *file, const char *output_path, int rewind_it) { return copy_vwindow(copy_flags, 0., tracks->total_length(), file, output_path, rewind_it); } int EDL::copy_xml(int copy_flags, double start, double end, FileXML *file, const char *closer, const char *output_path, int rewind_it) { file->append_tag(); file->append_newline(); // Set clipboard samples only if copying to clipboard if( (copy_flags & COPY_LENGTH) ) { file->tag.set_title("CLIPBOARD"); file->tag.set_property("LENGTH", end - start); file->append_tag(); file->tag.set_title("/CLIPBOARD"); file->append_tag(); file->append_newline(); file->append_newline(); } //printf("EDL::copy 1\n"); // Sessions if( (copy_flags & COPY_LOCAL_SESSION) ) local_session->save_xml(file, start); // Top level stuff. // Need to copy all this from child EDL if pasting is desired. if( (copy_flags & COPY_SESSION) ) session->save_xml(file); if( (copy_flags & COPY_VIDEO_CONFIG) ) session->save_video_config(file); if( (copy_flags & COPY_AUDIO_CONFIG) ) session->save_audio_config(file); if( (copy_flags & COPY_FOLDERS) ) folders.save_xml(file); if( (copy_flags & (COPY_ALL_ASSETS | COPY_USED_ASSETS)) ) copy_assets(copy_flags, start, end, file, output_path); if( (copy_flags & COPY_NESTED_EDL) ) { for( int i=0; icopy_nested(copy_flags, file, output_path, 0); } // Clips if( (copy_flags & COPY_CLIPS) ) { for( int i=0; icopy_clip(copy_flags, file, output_path, 0); } if( (copy_flags & COPY_VWINDOWS) ) { for( int i=0; icopy_vwindow(copy_flags, file, output_path, 0); } if( (copy_flags & COPY_MIXERS) ) mixers.save(file); file->append_newline(); file->append_newline(); if( (copy_flags & COPY_LABELS) ) labels->copy(start, end, file); tracks->copy(copy_flags, start, end, file, output_path); // terminate file file->tag.set_title(closer); file->append_tag(); file->append_newline(); // For editing operations we want to rewind it for immediate pasting. // For clips and saving to disk leave it alone. if( rewind_it ) { file->terminate_string(); file->rewind(); } return 0; } void EDL::copy_indexables(EDL *edl) { for( Track *track=edl->tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( edit->asset ) assets->update(edit->asset); if( edit->nested_edl ) nested_edls.get_nested(edit->nested_edl); } } } EDL *EDL::new_nested_edl(EDL *edl, const char *path) { EDL *nested = new EDL; // no parent for nested edl nested->create_objects(); nested->copy_session(edl); nested->set_path(path); nested->update_index(edl); nested->copy_indexables(edl); nested->tracks->copy_from(edl->tracks); nested_edls.append(nested); return nested; } EDL *EDL::get_nested_edl() { Track *track = tracks->first; Edit *edit = track ? track->edits->first : 0; EDL *nested = edit && !edit->next && !edit->asset ? edit->nested_edl : 0; while( nested && (track=track->next)!=0 ) { Edit *edit = track->edits->first; if( !edit || edit->next || ( edit->nested_edl != nested && strcmp(edit->nested_edl->path, nested->path) ) ) nested = 0; } return nested; } EDL *EDL::create_nested_clip(EDL *nested) { EDL *new_edl = new EDL(this); // parent for clip edl new_edl->create_objects(); new_edl->create_nested(nested); return new_edl; } void EDL::create_nested(EDL *nested) { int video_tracks = 0, audio_tracks = 0; for( Track *track=nested->tracks->first; track!=0; track=track->next ) { if( track->data_type == TRACK_VIDEO && track->record ) ++video_tracks; if( track->data_type == TRACK_AUDIO && track->record ) ++audio_tracks; } // renderengine properties if( video_tracks > 0 ) video_tracks = 1; if( audio_tracks > 0 ) audio_tracks = nested->session->audio_channels; // Keep frame rate, sample rate, and output size unchanged. // Nest all video & audio outputs session->video_channels = video_tracks; session->audio_channels = audio_tracks; session->video_tracks = 1; session->audio_tracks = audio_tracks; create_default_tracks(); insert_asset(0, nested, 0, 0, 0); } void EDL::overwrite_clip(EDL *clip) { int folder = folder_no; char clip_title[BCTEXTLEN], clip_notes[BCTEXTLEN], clip_icon[BCSTRLEN]; if( parent_edl ) { strcpy(clip_title, local_session->clip_title); strcpy(clip_notes, local_session->clip_notes); strcpy(clip_icon, local_session->clip_icon); } copy_all(clip); folder_no = folder; if( parent_edl ) { strcpy(local_session->clip_title, clip_title); strcpy(local_session->clip_notes, clip_notes); strcpy(local_session->clip_icon, clip_icon); } if( !clip_icon[0] ) return; // discard old clip icon to reconstruct char clip_icon_path[BCTEXTLEN]; snprintf(clip_icon_path, sizeof(clip_icon_path), "%s/%s", File::get_config_path(), clip_icon); remove(clip_icon_path); } void EDL::retrack() { int min_w = session->output_w, min_h = session->output_h; for( Track *track=tracks->first; track!=0; track=track->next ) { if( track->data_type != TRACK_VIDEO ) continue; int w = min_w, h = min_h; for( Edit *current=track->edits->first; current!=0; current=NEXT ) { Indexable* indexable = current->get_source(); if( !indexable ) continue; int edit_w = indexable->get_w(), edit_h = indexable->get_h(); if( w < edit_w ) w = edit_w; if( h < edit_h ) h = edit_h; } if( track->track_w == w && track->track_h == h ) continue; ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])-> translate_masks( (w - track->track_w) / 2, (h - track->track_h) / 2); track->track_w = w; track->track_h = h; } } void EDL::rechannel() { for( Track *current=tracks->first; current; current=NEXT ) { if( current->data_type == TRACK_AUDIO ) { PanAutos *autos = (PanAutos*)current->automation->autos[AUTOMATION_PAN]; ((PanAuto*)autos->default_auto)->rechannel(); for( PanAuto *keyframe = (PanAuto*)autos->first; keyframe; keyframe = (PanAuto*)keyframe->next ) { keyframe->rechannel(); } } } } void EDL::resample(double old_rate, double new_rate, int data_type) { for( Track *current=tracks->first; current; current=NEXT ) { if( current->data_type == data_type ) { current->resample(old_rate, new_rate); } } } void EDL::synchronize_params(EDL *edl) { local_session->synchronize_params(edl->local_session); for( Track *this_track=tracks->first, *that_track=edl->tracks->first; this_track && that_track; this_track=this_track->next, that_track=that_track->next ) { this_track->synchronize_params(that_track); } } int EDL::trim_selection(double start, double end, int edit_labels, int edit_plugins, int edit_autos) { if( start != end ) { // clear the data clear(0, start, edit_labels, edit_plugins, edit_autos); clear(end - start, tracks->total_length(), edit_labels, edit_plugins, edit_autos); } return 0; } int EDL::equivalent(double position1, double position2) { double threshold = session->cursor_on_frames ? 0.5 / session->frame_rate : 1.0 / session->sample_rate; return fabs(position2 - position1) < threshold ? 1 : 0; } double EDL::equivalent_output(EDL *edl) { double result = -1; session->equivalent_output(edl->session, &result); tracks->equivalent_output(edl->tracks, &result); return result; } void EDL::set_path(const char *path) { if( &this->path[0] == path ) return; strcpy(this->path, path); } void EDL::set_inpoint(double position) { if( equivalent(local_session->get_inpoint(), position) && local_session->get_inpoint() >= 0 ) { local_session->unset_inpoint(); } else { local_session->set_inpoint(align_to_frame(position, 0)); if( local_session->get_outpoint() <= local_session->get_inpoint() ) local_session->unset_outpoint(); } } void EDL::set_outpoint(double position) { if( equivalent(local_session->get_outpoint(), position) && local_session->get_outpoint() >= 0 ) { local_session->unset_outpoint(); } else { local_session->set_outpoint(align_to_frame(position, 0)); if( local_session->get_inpoint() >= local_session->get_outpoint() ) local_session->unset_inpoint(); } } void EDL::unset_inoutpoint() { local_session->unset_inpoint(); local_session->unset_outpoint(); } int EDL::blade(double position) { return tracks->blade(position); } int EDL::clear(double start, double end, int clear_labels, int clear_plugins, int edit_autos) { if( start == end ) { double distance = 0; tracks->clear_handle(start, end, distance, clear_labels, clear_plugins, edit_autos); if( clear_labels && distance > 0 ) labels->paste_silence(start, start + distance); } else { tracks->clear(start, end, clear_plugins, edit_autos); if( clear_labels ) labels->clear(start, end, 1); } // Need to put at beginning so a subsequent paste operation starts at the // right position. double position = local_session->get_selectionstart(); local_session->set_selectionend(position); local_session->set_selectionstart(position); return 0; } int EDL::clear_hard_edges(double start, double end) { return tracks->clear_hard_edges(start, end); } static int dead_edit_cmp(Edit**ap, Edit**bp) { Edit *a = *ap, *b = *bp; if( a->track != b->track ) return 0; return a->startproject > b->startproject ? -1 : 1; } void EDL::delete_edits(ArrayList *edits, int collapse) { edits->sort(dead_edit_cmp); for( int i=0; isize(); ++i ) { Edit *edit = edits->get(i); Track *track = edit->track; int64_t start = edit->startproject; int64_t length = edit->length; int64_t end = start + length; if( session->autos_follow_edits ) { track->automation->clear(start, end, 0, collapse); } if( session->plugins_follow_edits ) { for( int k=0; kplugin_set.size(); ++k ) { PluginSet *plugin_set = track->plugin_set[k]; plugin_set->clear(start, end, 1); if( !collapse ) plugin_set->paste_silence(start, end, 1); plugin_set->optimize(); } } Edit *dead_edit = edit; if( collapse ) { while( (edit=edit->next) ) edit->startproject -= length; } delete dead_edit; } optimize(); } class Range { public: static int cmp(Range *ap, Range *bp); double start, end; bool operator ==(Range &that) { return this->start == that.start; } bool operator >(Range &that) { return this->start > that.start; } }; int Range::cmp(Range *ap, Range *bp) { return ap->start < bp->start ? -1 : ap->start == bp->start ? 0 : 1; } static void get_edit_regions(ArrayList *edits, ArrayList ®ions) { // move edit inclusive labels by regions for( int i=0; isize(); ++i ) { Edit *edit = edits->get(i); double pos = edit->track->from_units(edit->startproject); double end = edit->track->from_units(edit->startproject + edit->length); int n = regions.size(), k = n; while( --k >= 0 ) { Range &range = regions[k]; if( pos >= range.end ) continue; if( range.start >= end ) continue; int expand = 0; if( range.start > pos ) { range.start = pos; expand = 1; } if( range.end < end ) { range.end = end; expand = 1; } if( !expand ) break; k = n; } if( k < 0 ) { Range &range = regions.append(); range.start = pos; range.end = end; } } regions.sort(Range::cmp); } void EDL::delete_edit_labels(ArrayList *edits, int collapse) { ArrayList regions; get_edit_regions(edits, regions); int n = regions.size(), k = n; while( --k >= 0 ) { Range &range = regions[k]; labels->clear(range.start, range.end, collapse); } } void EDL::move_edit_labels(ArrayList *edits, double dist) { ArrayList regions; get_edit_regions(edits, regions); int n = regions.size(), k = n; Labels moved(this, 0); while( --k >= 0 ) { Range &range = regions[k]; Label *label = labels->label_of(range.start); for( Label *next=0; label && label->position <= range.end; label=next ) { next = label->next; labels->remove_pointer(label); label->position += dist; moved.append(label); } Label *current = labels->first; while( (label=moved.first) ) { moved.remove_pointer(label); while( current && current->position < label->position ) current = current->next; if( current && current->position == label->position ) { delete label; continue; } labels->insert_before(current, label); } } } void EDL::modify_edithandles(double oldposition, double newposition, int currentend, int handle_mode, int edit_labels, int edit_plugins, int edit_autos, int group_id) { tracks->modify_edithandles(oldposition, newposition, currentend, handle_mode, edit_labels, edit_plugins, edit_autos, group_id); } void EDL::modify_pluginhandles(double oldposition, double newposition, int currentend, int handle_mode, int edit_labels, int edit_autos, Edits *trim_edits) { tracks->modify_pluginhandles(oldposition, newposition, currentend, handle_mode, edit_labels, edit_autos, trim_edits); optimize(); } void EDL::paste_silence(double start, double end, int edit_labels, int edit_plugins, int edit_autos) { if( edit_labels ) labels->paste_silence(start, end); tracks->paste_silence(start, end, edit_plugins, edit_autos); } void EDL::remove_from_project(ArrayList *clips) { for( int i=0; isize(); ++i ) { this->clips.remove_clip(clips->get(i)); } } void EDL::remove_from_project(ArrayList *assets) { // Remove from clips if( !parent_edl ) for( int j=0; jremove_from_project(assets); } // Remove from VWindow EDLs for( int i=0; iremove_from_project(assets); for( int i=0; isize(); ++i ) { // Remove from tracks for( Track *track=tracks->first; track; track=track->next ) { track->remove_asset(assets->get(i)); } // Remove from assets if( !parent_edl && assets->get(i)->is_asset ) { this->assets->remove_asset((Asset*)assets->get(i)); } else if( !parent_edl && !assets->get(i)->is_asset ) { this->nested_edls.remove_clip((EDL*)assets->get(i)); } } } void EDL::update_assets(EDL *src) { for( Asset *current=src->assets->first; current; current=NEXT ) { assets->update(current); } } int EDL::get_tracks_height(Theme *theme) { int total_pixels = 0; for( Track *current=tracks->first; current; current=NEXT ) { total_pixels += current->vertical_span(theme); } return total_pixels; } int64_t EDL::get_tracks_width() { int64_t total_pixels = 0; for( Track *current=tracks->first; current; current=NEXT ) { int64_t pixels = current->horizontal_span(); if( pixels > total_pixels ) total_pixels = pixels; } //printf("EDL::get_tracks_width %d\n", total_pixels); return total_pixels; } // int EDL::calculate_output_w(int single_channel) // { // if( single_channel ) return session->output_w; // // int widest = 0; // for( int i=0; ivideo_channels; ++i ) // { // if( session->vchannel_x[i] + session->output_w > widest ) widest = session->vchannel_x[i] + session->output_w; // } // return widest; // } // // int EDL::calculate_output_h(int single_channel) // { // if( single_channel ) return session->output_h; // // int tallest = 0; // for( int i=0; ivideo_channels; ++i ) // { // if( session->vchannel_y[i] + session->output_h > tallest ) tallest = session->vchannel_y[i] + session->output_h; // } // return tallest; // } // Get the total output size scaled to aspect ratio void EDL::calculate_conformed_dimensions(int single_channel, float &w, float &h) { if( (float)session->output_w / session->output_h > get_aspect_ratio() ) h = (w = session->output_w) / get_aspect_ratio(); else w = (h = session->output_h) * get_aspect_ratio(); } float EDL::get_aspect_ratio() { return session->aspect_w / session->aspect_h; } int EDL::dump(FILE *fp) { if( parent_edl ) fprintf(fp,"CLIP\n"); else fprintf(fp,"EDL\n"); fprintf(fp," clip_title: %s\n" " parent_edl: %p\n", local_session->clip_title, parent_edl); fprintf(fp," selectionstart %f\n selectionend %f\n loop_start %f\n loop_end %f\n", local_session->get_selectionstart(1), local_session->get_selectionend(1), local_session->loop_start, local_session->loop_end); for( int i=0; iview_start[i], local_session->track_start[i]); } if( !parent_edl ) { fprintf(fp,"audio_channels: %d audio_tracks: %d sample_rate: %jd\n", session->audio_channels, session->audio_tracks, session->sample_rate); fprintf(fp," video_channels: %d\n" " video_tracks: %d\n" " frame_rate: %.2f\n" " frames_per_foot: %.2f\n" " output_w: %d\n" " output_h: %d\n" " aspect_w: %f\n" " aspect_h: %f\n" " color_model: %d\n", session->video_channels, session->video_tracks, session->frame_rate, session->frames_per_foot, session->output_w, session->output_h, session->aspect_w, session->aspect_h, session->color_model); fprintf(fp," CLIPS"); fprintf(fp," total: %d\n", clips.size()); for( int i=0; idump(fp); fprintf(fp,"\n\n"); } fprintf(fp," NESTED_EDLS"); fprintf(fp," total: %d\n", nested_edls.size()); for( int i=0; ipath); fprintf(fp," VWINDOW EDLS"); fprintf(fp," total: %d\n", total_vwindow_edls()); for( int i=0; ilocal_session->clip_title); } fprintf(fp," ASSETS\n"); assets->dump(fp); } fprintf(fp," LABELS\n"); labels->dump(fp); fprintf(fp," TRACKS\n"); tracks->dump(fp); //printf("EDL::dump 2\n"); return 0; } EDL* EDL::add_clip(EDL *edl) { // Copy argument. New edls are deleted from MWindow::load_filenames. EDL *new_edl = new EDL(this); new_edl->create_objects(); new_edl->copy_all(edl); new_edl->folder_no = AW_CLIP_FOLDER; clips.append(new_edl); return new_edl; } void EDL::insert_asset(Asset *asset, EDL *nested_edl, double position, Track *first_track, RecordLabels *labels) { // Insert asset into asset table Asset *new_asset = 0; EDL *new_nested_edl = 0; if( asset ) new_asset = assets->update(asset); if( nested_edl ) new_nested_edl = nested_edls.get_nested(nested_edl); // Paste video int vtrack = 0; Track *current = first_track ? first_track : tracks->first; // Fix length of single frame double length = 0.; int layers = 0; int channels = 0; if( new_nested_edl ) { length = new_nested_edl->tracks->total_length(); layers = new_nested_edl->session->video_channels; channels = new_nested_edl->session->audio_channels; } if( new_asset ) { // Insert 1 frame for undefined length if( new_asset->video_length < 0 ) { length = session->si_useduration ? session->si_duration : 1.0 / session->frame_rate; } else { length = new_asset->frame_rate > 0 ? (double)new_asset->video_length / new_asset->frame_rate : 1.0 / session->frame_rate; } layers = new_asset->layers; channels = new_asset->channels; } for( ; current && vtrackrecord || current->data_type != TRACK_VIDEO ) continue; current->insert_asset(new_asset, new_nested_edl, length, position, vtrack++); } int atrack = 0; if( new_asset ) { if( new_asset->audio_length < 0 ) { // Insert 1 frame for undefined length & video if( new_asset->video_data ) length = (double)1.0 / new_asset->frame_rate; else // Insert 1 second for undefined length & no video length = 1.0; } else length = (double)new_asset->audio_length / new_asset->sample_rate; } current = tracks->first; for( ; current && atrack < channels; current=NEXT ) { if( !current->record || current->data_type != TRACK_AUDIO ) continue; current->insert_asset(new_asset, new_nested_edl, length, position, atrack++); } // Insert labels from a recording window. if( labels ) { for( RecordLabel *label=labels->first; label; label=label->next ) { this->labels->toggle_label(label->position, label->position); } } } void EDL::set_index_file(Indexable *indexable) { if( indexable->is_asset ) assets->update_index((Asset*)indexable); else nested_edls.update_index((EDL*)indexable); } void EDL::optimize() { //printf("EDL::optimize 1\n"); double length = tracks->total_length(); double preview_start = local_session->preview_start; double preview_end = local_session->preview_end; if( preview_end < 0 || preview_end > length ) preview_end = length; if( preview_start == 0 && preview_end >= length ) local_session->preview_end = -1; if( preview_start > preview_end ) local_session->preview_start = preview_end; for( Track *current=tracks->first; current; current=NEXT ) current->optimize(); } int EDL::next_id() { static Mutex id_lock; id_lock.lock("EDL::next_id"); int result = EDLSession::current_id++; id_lock.unlock(); return result; } void EDL::get_shared_plugins(Track *source, ArrayList *plugin_locations, int omit_recordable, int data_type) { for( Track *track=tracks->first; track; track=track->next ) { if( track->record && omit_recordable ) continue; if( track == source || track->data_type != data_type ) continue; for( int i=0; iplugin_set.size(); ++i ) { Plugin *plugin = track->get_current_plugin( local_session->get_selectionstart(1), i, PLAY_FORWARD, 1, 0); if( plugin && plugin->plugin_type != PLUGIN_STANDALONE ) continue; plugin_locations->append(new SharedLocation(tracks->number_of(track), i)); } } } void EDL::get_shared_tracks(Track *track, ArrayList *module_locations, int omit_recordable, int data_type) { for( Track *current=tracks->first; current; current=NEXT ) { if( omit_recordable && current->record ) continue; if( current == track || current->data_type != data_type ) continue; module_locations->append(new SharedLocation(tracks->number_of(current), 0)); } } // aligned frame time, account for sample truncation double EDL::frame_align(double position, int round) { if( !round && session->sample_rate > 0 ) { int64_t sample_pos = position * session->sample_rate; position = (sample_pos+2.) / session->sample_rate; } int64_t frame_pos = (position * session->frame_rate + (round ? 0.5 : 1e-6)); return frame_pos / session->frame_rate; } // Convert position to frames if alignment is enabled. double EDL::align_to_frame(double position, int round) { if( session->cursor_on_frames ) position = frame_align(position, round); return position; } BinFolder *EDL::get_folder(int no) { for( int i=0; iawindow_folder ) return fp; } return 0; } int EDL::get_folder_number(const char *title) { for( int i=0; ititle) ) return folders[i]->awindow_folder; } return AW_NO_FOLDER; } const char *EDL::get_folder_name(int no) { if( no >= 0 && notitle; } int EDL::new_folder(const char *title, int is_clips) { if( !title[0] ) return 1; int ret = get_folder_number(title); if( ret >= 0 ) return 1; int idx = AWINDOW_FOLDERS; for( int i=0; iawindow_folder; if( no >= idx ) idx = no+1; } folders.append(new BinFolder(idx, is_clips, title)); return 0; } int EDL::delete_folder(const char *title) { int k = folders.size(); while( --k >= 0 && strcmp(title, folders[k]->title) ); if( k >= 0 ) folders.remove_object_number(k); return k; } int EDL::get_use_vconsole(VEdit* *playable_edit, int64_t position, int direction, PlayableTracks *playable_tracks) { int share_playable_tracks = 1; int result = 0; VTrack *playable_track = 0; const int debug = 0; *playable_edit = 0; // Calculate playable tracks when being called as a nested EDL if( !playable_tracks ) { share_playable_tracks = 0; playable_tracks = new PlayableTracks(this, position, direction, TRACK_VIDEO, 1); } // Total number of playable tracks is 1 if( playable_tracks->size() != 1 ) { result = 1; } else { playable_track = (VTrack*)playable_tracks->get(0); } // Don't need playable tracks anymore if( !share_playable_tracks ) { delete playable_tracks; } if( debug ) printf("EDL::get_use_vconsole %d playable_tracks->size()=%d\n", __LINE__, playable_tracks->size()); if( result ) return 1; // Test mutual conditions between direct copy rendering and this. if( !playable_track->direct_copy_possible(position, direction, 1) ) return 1; if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__); *playable_edit = (VEdit*)playable_track->edits->editof(position, direction, 0); // No edit at current location if( !*playable_edit ) return 1; if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__); // Edit is nested EDL if( (*playable_edit)->nested_edl ) { // Test nested EDL EDL *nested_edl = (*playable_edit)->nested_edl; int64_t nested_position = (int64_t)((position - (*playable_edit)->startproject + (*playable_edit)->startsource) * nested_edl->session->frame_rate / session->frame_rate); VEdit *playable_edit_temp = 0; if( session->output_w != nested_edl->session->output_w || session->output_h != nested_edl->session->output_h || nested_edl->get_use_vconsole(&playable_edit_temp, nested_position, direction, 0) ) return 1; return 0; } if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__); // Edit is not a nested EDL Asset *asset = (*playable_edit)->asset; // Edit is silence if( !asset ) return 1; if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__); // Asset and output device must have the same dimensions if( asset->width != session->output_w || asset->height != session->output_h ) return 1; if( debug ) printf("EDL::get_use_vconsole %d\n", __LINE__); // If we get here the frame is going to be directly copied. Whether it is // decompressed in hardware depends on the colormodel. return 0; } // For Indexable int EDL::get_audio_channels() { return session->audio_channels; } int EDL::get_sample_rate() { return session->sample_rate; } int64_t EDL::get_audio_samples() { return (int64_t)(tracks->total_length() * session->sample_rate); } int EDL::have_audio() { return 1; } int EDL::have_video() { return 1; } int EDL::get_w() { return session->output_w; } int EDL::get_h() { return session->output_h; } double EDL::get_frame_rate() { return session->frame_rate; } int EDL::get_video_layers() { return 1; } int64_t EDL::get_video_frames() { return (int64_t)(tracks->total_length() * session->frame_rate); } void EDL::remove_vwindow_edls() { for( int i=0; iremove_user(); } vwindow_edls.remove_all(); } void EDL::remove_vwindow_edl(EDL *edl) { if( vwindow_edls.number_of(edl) >= 0 ) { edl->remove_user(); vwindow_edls.remove(edl); } } EDL* EDL::get_vwindow_edl(int number) { return vwindow_edls.get(number); } int EDL::total_vwindow_edls() { return vwindow_edls.size(); } void EDL::append_vwindow_edl(EDL *edl, int increase_counter) { if(vwindow_edls.number_of(edl) >= 0) return; if(increase_counter) edl->add_user(); vwindow_edls.append(edl); } double EDL::next_edit(double position) { Units::fix_double(&position); double new_position = tracks->total_length(); double max_rate = get_frame_rate(); int sample_rate = get_sample_rate(); if( sample_rate > max_rate ) max_rate = sample_rate; double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6; // Test for edit handles after position for( Track *track=tracks->first; track; track=track->next ) { if( !track->record ) continue; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { double edit_end = track->from_units(edit->startproject + edit->length); Units::fix_double(&edit_end); if( fabs(edit_end-position) < min_movement ) continue; if( edit_end > position && edit_end < new_position ) new_position = edit_end; } } return new_position; } double EDL::prev_edit(double position) { Units::fix_double(&position); double new_position = -1; double max_rate = get_frame_rate(); int sample_rate = get_sample_rate(); if( sample_rate > max_rate ) max_rate = sample_rate; double min_movement = max_rate > 0 ? 1. / max_rate : 1e-6; // Test for edit handles before cursor position for( Track *track=tracks->first; track; track=track->next ) { if( !track->record ) continue; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { double edit_end = track->from_units(edit->startproject); Units::fix_double(&edit_end); if( fabs(edit_end-position) < min_movement ) continue; if( edit_end < position && edit_end > new_position ) new_position = edit_end; } } return new_position; } double EDL::skip_silence(double position) { double result = position, nearest = DBL_MAX; for( Track *track=tracks->first; track; track=track->next ) { if( !track->play ) continue; Edit *edit = track->edits->editof(position, PLAY_FORWARD, 0); while( edit && edit->silence() ) edit = edit->next; if( !edit ) continue; double pos = track->from_units(edit->startproject); if( pos > position && pos < nearest ) nearest = result = pos; } return result; } void EDL::rescale_proxy(int orig_scale, int new_scale) { if( orig_scale == new_scale ) return; // project size float orig_w = (float)session->output_w * orig_scale; float orig_h = (float)session->output_h * orig_scale; if( !parent_edl ) { session->output_w = Units::round(orig_w / new_scale); session->output_h = Units::round(orig_h / new_scale); } // track sizes for( Track *track=tracks->first; track; track=track->next ) { if( track->data_type != TRACK_VIDEO ) continue; orig_w = (float)track->track_w * orig_scale; orig_h = (float)track->track_h * orig_scale; track->track_w = Units::round(orig_w / new_scale); track->track_h = Units::round(orig_h / new_scale); ((MaskAutos*)track->automation->autos[AUTOMATION_MASK])-> set_proxy(orig_scale, new_scale); ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_X])-> set_proxy(orig_scale, new_scale); ((FloatAutos*)track->automation->autos[AUTOMATION_CAMERA_Y])-> set_proxy(orig_scale, new_scale); ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_X])-> set_proxy(orig_scale, new_scale); ((FloatAutos*)track->automation->autos[AUTOMATION_PROJECTOR_Y])-> set_proxy(orig_scale, new_scale); } } void EDL::set_proxy(int new_scale, int new_use_scaler, ArrayList *orig_assets, ArrayList *proxy_assets) { int orig_scale = session->proxy_scale; session->proxy_scale = new_scale; int orig_use_scaler = session->proxy_use_scaler; session->proxy_use_scaler = new_use_scaler; if( orig_use_scaler ) orig_scale = 1; int scale = new_use_scaler ? new_scale : 1; int asset_scale = new_scale == 1 && !new_use_scaler ? 0 : scale; // change original assets to proxy assets int folder_no = new_use_scaler || new_scale != 1 ? AW_PROXY_FOLDER : AW_MEDIA_FOLDER; for( int i=0,n=proxy_assets->size(); iget(i); proxy_idxbl->folder_no = folder_no; if( !proxy_idxbl->is_asset ) continue; Asset *proxy_asset = assets->update((Asset *)proxy_idxbl); proxy_asset->width = proxy_asset->actual_width * scale; proxy_asset->height = proxy_asset->actual_height * scale; proxy_asset->proxy_scale = asset_scale; } // rescale to full size asset in read_frame if( new_use_scaler ) new_scale = 1; rescale_proxy(orig_scale, new_scale); // replace track contents for( Track *track=tracks->first; track; track=track->next ) { if( track->data_type != TRACK_VIDEO ) continue; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { Indexable *idxbl = (Indexable *)edit->asset; if( !idxbl ) idxbl = (Indexable *)edit->nested_edl; if( !idxbl ) continue; int i = orig_assets->size(); while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) ); if( i < 0 ) continue; Indexable *proxy_idxbl = proxy_assets->get(i); Asset *proxy_asset = proxy_idxbl->is_asset ? assets->update((Asset *)proxy_idxbl) : 0; EDL *proxy_edl = !proxy_idxbl->is_asset ? (EDL *)proxy_idxbl : 0; edit->asset = proxy_asset; edit->nested_edl = proxy_edl; } } for( int j=0,n=clips.size(); jtracks->first; track; track=track->next ) { if( track->data_type != TRACK_VIDEO ) continue; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { Indexable *idxbl = (Indexable *)edit->asset; if( !idxbl ) idxbl = (Indexable *)edit->nested_edl; if( !idxbl ) continue; int i = orig_assets->size(); while( --i>=0 && strcmp(orig_assets->get(i)->path, idxbl->path) ); if( i < 0 ) continue; Indexable *proxy_idxbl = proxy_assets->get(i); Asset *proxy_asset = proxy_idxbl->is_asset ? assets->update((Asset *)proxy_idxbl) : 0; EDL *proxy_edl = !proxy_idxbl->is_asset ? (EDL *)proxy_idxbl : 0; edit->asset = proxy_asset; edit->nested_edl = proxy_edl; has_proxy = 1; } } if( has_proxy && !orig_use_scaler ) clip->rescale_proxy(orig_scale, new_scale); } } void EDL::add_proxy(ArrayList *orig_assets, ArrayList *proxy_assets) { int asset_scale = session->proxy_scale; if( asset_scale == 1 ) asset_scale = 0; int scale = !asset_scale ? 1 : asset_scale; // update proxy geom using scale for( int i=0; isize(); ++i ) { Asset *proxy_asset = (Asset *)proxy_assets->get(i); proxy_asset->proxy_scale = asset_scale; proxy_asset->width = proxy_asset->actual_width * scale; proxy_asset->height = proxy_asset->actual_height * scale; } // change original assets to proxy assets for( int i=0,n=proxy_assets->size(); iupdate((Asset *)proxy_assets->get(i)); proxy_asset->folder_no = AW_PROXY_FOLDER; // replace track contents for( Track *track=tracks->first; track; track=track->next ) { if( track->data_type != TRACK_VIDEO ) continue; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( !edit->asset ) continue; if( !strcmp(edit->asset->path, orig_assets->get(i)->path) ) { edit->asset = proxy_asset; } } } } } Asset *EDL::get_proxy_asset() { return folder_no == AW_PROXY_FOLDER ? tracks->first->edits->first->asset : 0; } Track *EDL::add_new_track(int data_type) { Track *new_track = 0; switch( data_type ) { case TRACK_VIDEO: ++session->video_tracks; new_track = tracks->add_video_track(0, 0); break; case TRACK_AUDIO: ++session->audio_tracks; new_track = tracks->add_audio_track(0, 0); break; case TRACK_SUBTITLE: new_track = tracks->add_subttl_track(0, 0); break; } return new_track; } double EDL::get_cursor_position(int cursor_x, int pane_no) { return (double)cursor_x * local_session->zoom_sample / session->sample_rate + (double)local_session->view_start[pane_no] * local_session->zoom_sample / session->sample_rate; } int64_t EDL::get_position_cursorx(double position, int pane_no) { return (int64_t)(position * session->sample_rate / local_session->zoom_sample) - local_session->view_start[pane_no]; } int EDL::in_use(Indexable *indexable) { for( Track *track=tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { Indexable *idxbl = (Indexable *)edit->asset; if( !idxbl ) idxbl = (Indexable *)edit->nested_edl; if( !idxbl ) continue; if( idxbl->id == indexable->id ) return 1; if( !indexable->is_asset != !idxbl->is_asset ) continue; if( !strcmp(idxbl->path, indexable->path) ) return 1; } } for( int i=0; iin_use(indexable) ) return 1; for( int i=0; iin_use(indexable) ) return 1; return 0; } int EDL::regroup(int next_id) { ArrayList new_groups; for( Track *track=tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( !edit->group_id ) continue; int k = new_groups.size(); while( --k >= 0 && new_groups[k] != edit->group_id ); if( k >= 0 ) continue; new_groups.append(edit->group_id); } } for( Track *track=tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( !edit->group_id ) continue; int k = new_groups.size(); while( --k >= 0 && new_groups[k] != edit->group_id ); if( k < 0 ) continue; edit->group_id = k + next_id; } } return new_groups.size(); } EDL *EDL::selected_edits_to_clip(int packed, double *start_position, Track **start_track, int edit_labels, int edit_autos, int edit_plugins) { double start = DBL_MAX, end = DBL_MIN; Track *first_track=0, *last_track = 0; for( Track *track=tracks->first; track; track=track->next ) { if( !track->record ) continue; int empty = 1; for( Edit *edit=track->edits->first; edit; edit=edit->next ) { if( !edit->is_selected || edit->silence() ) continue; double edit_pos = track->from_units(edit->startproject); if( start > edit_pos ) start = edit_pos; if( end < (edit_pos+=edit->length) ) end = edit_pos; empty = 0; } if( empty ) continue; if( !first_track ) first_track = track; last_track = track; } if( start_position ) *start_position = start; if( start_track ) *start_track = first_track; if( !first_track ) return 0; EDL *new_edl = new EDL(); new_edl->create_objects(); new_edl->copy_session(this); const char *text = _("new_edl edit"); new_edl->set_path(text); strcpy(new_edl->local_session->clip_title, text); strcpy(new_edl->local_session->clip_notes, text); new_edl->session->video_tracks = 0; new_edl->session->audio_tracks = 0; for( Track *track=tracks->first; track; track=track->next ) { if( !track->record ) continue; if( first_track ) { if( first_track != track ) continue; first_track = 0; } Track *new_track = 0; if( !packed ) new_track = new_edl->add_new_track(track->data_type); int64_t start_pos = track->to_units(start, 0); int64_t end_pos = track->to_units(end, 0); int64_t startproject = 0; Edit *edit = track->edits->first; for( ; edit; edit=edit->next ) { if( !edit->is_selected || edit->silence() ) continue; if( edit->startproject < start_pos ) continue; if( edit->startproject >= end_pos ) break; int64_t edit_start_pos = edit->startproject; int64_t edit_end_pos = edit->startproject + edit->length; if( !new_track ) new_track = new_edl->add_new_track(track->data_type); int64_t edit_pos = edit_start_pos - start_pos; if( !packed && edit_pos > startproject ) { Edit *silence = new Edit(new_edl, new_track); silence->startproject = startproject; silence->length = edit_pos - startproject; new_track->edits->append(silence); startproject = edit_pos; } int64_t clip_start_pos = startproject; Edit *clip_edit = new Edit(new_edl, new_track); clip_edit->clone_from(edit); clip_edit->startproject = startproject; startproject += clip_edit->length; new_track->edits->append(clip_edit); if( edit_labels ) { double edit_start = track->from_units(edit_start_pos); double edit_end = track->from_units(edit_end_pos); double clip_start = new_track->from_units(clip_start_pos); Label *label = labels->first; for( ; label; label=label->next ) { if( label->position < edit_start ) continue; if( label->position >= edit_end ) break; double clip_position = label->position - edit_start + clip_start; Label *clip_label = new_edl->labels->first; while( clip_label && clip_label->positionnext; if( clip_label && clip_label->position == clip_position ) continue; Label *new_label = new Label(new_edl, new_edl->labels, clip_position, label->textstr); new_edl->labels->insert_before(clip_label, new_label); } } if( edit_autos ) { Automation *automation = track->automation; Automation *new_automation = new_track->automation; for( int i=0; iautos[i]; if( !autos ) continue; Autos *new_autos = new_automation->autos[i]; new_autos->default_auto->copy_from(autos->default_auto); Auto *aut0 = autos->first; for( ; aut0; aut0=aut0->next ) { if( aut0->position < edit_start_pos ) continue; if( aut0->position >= edit_end_pos ) break; Auto *new_auto = new_autos->new_auto(); new_auto->copy_from(aut0); int64_t clip_position = aut0->position - edit_start_pos + clip_start_pos; new_auto->position = clip_position; new_autos->append(new_auto); } } } if( edit_plugins ) { while( new_track->plugin_set.size() < track->plugin_set.size() ) new_track->plugin_set.append(0); for( int i=0; iplugin_set.total; ++i ) { PluginSet *plugin_set = track->plugin_set[i]; if( !plugin_set ) continue; PluginSet *new_plugin_set = new_track->plugin_set[i]; if( !new_plugin_set ) { new_plugin_set = new PluginSet(new_edl, new_track); new_track->plugin_set[i] = new_plugin_set; } Plugin *plugin = (Plugin*)plugin_set->first; int64_t startplugin = new_plugin_set->length(); for( ; plugin ; plugin=(Plugin*)plugin->next ) { if( plugin->silence() ) continue; int64_t plugin_start_pos = plugin->startproject; int64_t plugin_end_pos = plugin_start_pos + plugin->length; if( plugin_end_pos < start_pos ) continue; if( plugin_start_pos > end_pos ) break; if( plugin_start_pos < edit_start_pos ) plugin_start_pos = edit_start_pos; if( plugin_end_pos > edit_end_pos ) plugin_end_pos = edit_end_pos; if( plugin_start_pos >= plugin_end_pos ) continue; int64_t plugin_pos = plugin_start_pos - start_pos; if( !packed && plugin_pos > startplugin ) { Plugin *silence = new Plugin(new_edl, new_track, ""); silence->startproject = startplugin; silence->length = plugin_pos - startplugin; new_plugin_set->append(silence); startplugin = plugin_pos; } Plugin *new_plugin = new Plugin(new_edl, new_track, plugin->title); new_plugin->copy_base(plugin); new_plugin->startproject = startplugin; new_plugin->length = plugin_end_pos - plugin_start_pos; startplugin += new_plugin->length; new_plugin_set->append(new_plugin); KeyFrames *keyframes = plugin->keyframes; KeyFrames *new_keyframes = new_plugin->keyframes; new_keyframes->default_auto->copy_from(keyframes->default_auto); new_keyframes->default_auto->position = new_plugin->startproject; KeyFrame *keyframe = (KeyFrame*)keyframes->first; for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { if( keyframe->position < edit_start_pos ) continue; if( keyframe->position >= edit_end_pos ) break; KeyFrame *clip_keyframe = new KeyFrame(new_edl, new_keyframes); clip_keyframe->copy_from(keyframe); int64_t key_position = keyframe->position - start_pos; if( packed ) key_position += new_plugin->startproject - plugin_pos; clip_keyframe->position = key_position; new_keyframes->append(clip_keyframe); } } } } } if( last_track == track ) break; } return new_edl; } EDL *EDL::selected_edits_to_clip(int packed, double *start_position, Track **start_track) { return selected_edits_to_clip(packed, start_position, start_track, session->labels_follow_edits, session->autos_follow_edits, session->plugins_follow_edits); } void EDL::paste_edits(EDL *clip, Track *first_track, double position, int overwrite, int edit_edits, int edit_labels, int edit_autos, int edit_plugins) { if( !first_track ) first_track = tracks->first; Track *src = clip->tracks->first; for( Track *track=first_track; track && src; track=track->next ) { if( !track->record ) continue; int64_t pos = track->to_units(position, 0); if( edit_edits ) { for( Edit *edit=src->edits->first; edit; edit=edit->next ) { if( edit->silence() ) continue; int64_t start = pos + edit->startproject; int64_t len = edit->length, end = start + len; if( overwrite ) track->edits->clear(start, end); Edit *dst = track->edits->insert_new_edit(start); dst->clone_from(edit); dst->startproject = start; dst->is_selected = 1; while( (dst=dst->next) != 0 ) dst->startproject += edit->length; if( overwrite ) continue; if( edit_labels && track == first_track ) { double dst_pos = track->from_units(start); double dst_len = track->from_units(len); for( Label *label=labels->first; label; label=label->next ) { if( label->position >= dst_pos ) label->position += dst_len; } } if( edit_autos ) { for( int i=0; iautomation->autos[i]; if( !autos ) continue; for( Auto *aut0=autos->first; aut0; aut0=aut0->next ) { if( aut0->position >= start ) aut0->position += edit->length; } } } if( edit_plugins ) { for( int i=0; iplugin_set.size(); ++i ) { PluginSet *plugin_set = track->plugin_set[i]; Plugin *plugin = (Plugin *)plugin_set->first; for( ; plugin; plugin=(Plugin *)plugin->next ) { if( plugin->startproject >= start ) plugin->startproject += edit->length; else if( plugin->startproject+plugin->length > end ) plugin->length += edit->length; Auto *default_keyframe = plugin->keyframes->default_auto; if( default_keyframe->position >= start ) default_keyframe->position += edit->length; KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first; for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { if( keyframe->position >= start ) keyframe->position += edit->length; } } plugin_set->optimize(); } } } } if( edit_autos ) { for( int i=0; iautomation->autos[i]; if( !src_autos ) continue; Autos *autos = track->automation->autos[i]; for( Auto *aut0=src_autos->first; aut0; aut0=aut0->next ) { int64_t auto_pos = pos + aut0->position; autos->insert_auto(auto_pos, aut0); } } } if( edit_plugins ) { for( int i=0; iplugin_set.size(); ++i ) { PluginSet *plugin_set = src->plugin_set[i]; if( !plugin_set ) continue; while( i >= track->plugin_set.size() ) track->plugin_set.append(0); PluginSet *dst_plugin_set = track->plugin_set[i]; if( !dst_plugin_set ) { dst_plugin_set = new PluginSet(this, track); track->plugin_set[i] = dst_plugin_set; } Plugin *plugin = (Plugin *)plugin_set->first; if( plugin ) track->expand_view = 1; for( ; plugin; plugin=(Plugin *)plugin->next ) { int64_t start = pos + plugin->startproject; int64_t end = start + plugin->length; if( overwrite || edit_edits ) dst_plugin_set->clear(start, end, 1); Plugin *new_plugin = dst_plugin_set->insert_plugin(plugin->title, start, end-start, plugin->plugin_type, &plugin->shared_location, (KeyFrame*)plugin->keyframes->default_auto, 0); new_plugin->on = plugin->on; KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first; for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { int64_t keyframe_pos = pos + keyframe->position; new_plugin->keyframes->insert_auto(keyframe_pos, keyframe); } while( (new_plugin=(Plugin *)new_plugin->next) ) { KeyFrame *keyframe = (KeyFrame*)new_plugin->keyframes->first; for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) keyframe->position += plugin->length; } } } } src = src->next; } if( edit_labels ) { Label *edl_label = labels->first; for( Label *label=clip->labels->first; label; label=label->next ) { double label_pos = position + label->position; int exists = 0; while( edl_label && !(exists=equivalent(edl_label->position, label_pos)) && edl_label->position < position ) edl_label = edl_label->next; if( exists ) continue; labels->insert_before(edl_label, new Label(this, labels, label_pos, label->textstr)); } } optimize(); } void EDL::paste_edits(EDL *clip, Track *first_track, double position, int overwrite) { paste_edits(clip, first_track, position, overwrite, 1, session->labels_follow_edits, session->autos_follow_edits, session->plugins_follow_edits); } void EDL::replace_assets(ArrayList &orig_idxbls, ArrayList &new_assets) { for( Track *track=tracks->first; track; track=track->next ) { for( Edit *edit=track->edits->first; edit; edit=edit->next ) { Indexable *idxbl = (Indexable *)edit->asset; if( !idxbl ) continue; int i = orig_idxbls.size(); while( --i>=0 && strcmp(orig_idxbls[i]->path, idxbl->path) ); if( i < 0 ) continue; Asset *new_asset = new_assets[i]; if( track->data_type == TRACK_VIDEO && !new_asset->video_data ) continue; if( track->data_type == TRACK_AUDIO && !new_asset->audio_data ) continue; edit->asset = assets->update(new_assets[i]); } } if( !parent_edl ) { for( int j=0,n=clips.size(); jreplace_assets(orig_idxbls, new_assets); } } int EDL::collect_effects(EDL *&group) { // to remap shared plugins in copied plugin stack edl_shared_list shared_map; int ret = 0; EDL *new_edl = new EDL(parent_edl ? parent_edl : this); new_edl->create_objects(); Tracks *new_tracks = new_edl->tracks; Track *track = tracks->first; for( ; !ret && track; track=track->next ) { if( track->data_type != TRACK_AUDIO && track->data_type != TRACK_VIDEO ) continue; Edit *edit = track->edits->first; while( edit && !edit->is_selected ) edit = edit->next; if( !edit ) continue; if( !track->record ) { ret = COLLECT_EFFECTS_RECORD; break; } Track *new_track = 0; edl_shared *location = 0; int64_t start_pos = edit->startproject; int64_t end_pos = start_pos + edit->length; int pluginsets = track->plugin_set.size(); for( int i=0; iplugin_set[i]; Plugin *plugin = (Plugin *)plugins->first; for( ; plugin; plugin=(Plugin *)plugin->next ) { if( plugin->silence() ) continue; if( start_pos < plugin->startproject+plugin->length ) break; } if( !plugin || plugin->startproject >= end_pos ) continue; if( !location ) { location = &shared_map.append(); location->trk = tracks->number_of(track); } location->append(i); if( !new_track ) new_track = track->data_type == TRACK_AUDIO ? new_tracks->add_audio_track(0, 0) : new_tracks->add_video_track(0, 0) ; PluginSet *new_plugins = new PluginSet(new_edl, new_track); new_track->plugin_set.append(new_plugins); Plugin *new_plugin = (Plugin *)new_plugins->create_edit(); new_plugin->copy_base(plugin); new_plugins->append(new_plugin); KeyFrame *keyframe = plugin->keyframes-> get_prev_keyframe(start_pos, PLAY_FORWARD); KeyFrame *default_auto = (KeyFrame *) new_plugin->keyframes->default_auto; default_auto->copy_from(keyframe); default_auto->position = 0; default_auto->is_default = 1; } if( !new_track ) { ret = COLLECT_EFFECTS_MISSING; break; } while( (edit=edit->next) && !edit->is_selected ); if( edit ) ret = COLLECT_EFFECTS_MULTIPLE; } track = new_edl->tracks->first; if( !ret && !track ) ret = COLLECT_EFFECTS_EMPTY; // remap shared plugins in copied new_edl for( ; !ret && track; track=track->next ) { int pluginsets = track->plugin_set.size(); for( int i=0; iplugin_set[i]; Plugin *plugin = (Plugin *)plugins->first; for( ; plugin; plugin=(Plugin *)plugin->next ) { if( plugin->plugin_type != PLUGIN_SHAREDPLUGIN ) continue; int trk = plugin->shared_location.module; int set = plugin->shared_location.plugin; int m = shared_map.size(), n = -1; while( --m>=0 && shared_map[m].trk!=trk ); if( m >= 0 ) { edl_shared &location = shared_map[m]; n = location.size(); while( --n>=0 && location[n]!=set ); } if( n < 0 ) { ret = COLLECT_EFFECTS_MASTER; break; } plugin->shared_location.module = m; plugin->shared_location.plugin = n; } } } if( !ret ) group = new_edl; else new_edl->remove_user(); return ret; } void edl_SharedLocations::add(int trk, int plg) { SharedLocation &s = append(); s.module = trk; s.plugin = plg; } // inserts pluginsets in group to first selected edit in tracks int EDL::insert_effects(EDL *group, Track *first_track) { edl_SharedLocations edl_locs, new_locs; Track *new_track = group->tracks->first; if( !first_track ) first_track = tracks->first; Track *track = first_track; for( ; track && new_track; track=track->next ) { Edit *edit = track->edits->first; while( edit && !edit->is_selected ) edit = edit->next; if( !edit ) continue; if( !track->record ) return INSERT_EFFECTS_RECORD; if( track->data_type != new_track->data_type ) return INSERT_EFFECTS_TYPE; int gtrk = group->tracks->number_of(new_track); int trk = tracks->number_of(track); int psz = track->plugin_set.size(); int new_pluginsets = new_track->plugin_set.size(); for( int i=0; inext) && !edit->is_selected ); if( edit ) return INSERT_EFFECTS_MULTIPLE; new_track = new_track->next; } if( new_track ) return INSERT_EFFECTS_MISSING; for( ; track; track=track->next ) { Edit *edit = track->edits->first; while( edit && !edit->is_selected ) edit = edit->next; if( edit ) return INSERT_EFFECTS_EXTRA; } new_track = group->tracks->first; track = first_track; for( ; track && new_track; track=track->next ) { if( !track->record ) continue; Edit *edit = track->edits->first; while( edit && !edit->is_selected ) edit = edit->next; if( !edit ) continue; int64_t start_pos = edit->startproject; int64_t end_pos = start_pos + edit->length; int new_pluginsets = new_track->plugin_set.size(); for( int i=0; iplugin_set[i]->first; if( !new_plugin ) continue; PluginSet *plugins = new PluginSet(this, track); track->plugin_set.append(plugins); SharedLocation shared_location; if( new_plugin->plugin_type == PLUGIN_SHAREDPLUGIN ) { SharedLocation &new_loc = new_plugin->shared_location; int k = new_locs.size(); while( --k>=0 && !new_loc.equivalent(&new_locs[k]) ); if( k < 0 ) return INSERT_EFFECTS_MASTER; shared_location.copy_from(&edl_locs[k]); } KeyFrame *default_keyframe = (KeyFrame *)new_plugin->keyframes->default_auto; plugins->insert_plugin(new_plugin->title, start_pos, end_pos - start_pos, new_plugin->plugin_type, &shared_location, default_keyframe, 0); track->expand_view = 1; } new_track = new_track->next; } return 0; }