ins mixers, match all and move for mixer align, cr in xml string kludge, save_as...
authorGood Guy <good1.2guy@gmail.com>
Sun, 31 Mar 2019 01:24:03 +0000 (19:24 -0600)
committerGood Guy <good1.2guy@gmail.com>
Sun, 31 Mar 2019 01:24:03 +0000 (19:24 -0600)
19 files changed:
cinelerra-5.1/cinelerra/assetpopup.C
cinelerra-5.1/cinelerra/assetpopup.h
cinelerra-5.1/cinelerra/assetpopup.inc
cinelerra-5.1/cinelerra/edits.C
cinelerra-5.1/cinelerra/edl.C
cinelerra-5.1/cinelerra/edl.h
cinelerra-5.1/cinelerra/filexml.C
cinelerra-5.1/cinelerra/mixersalign.C
cinelerra-5.1/cinelerra/mixersalign.h
cinelerra-5.1/cinelerra/mixersalign.inc
cinelerra-5.1/cinelerra/mwindow.C
cinelerra-5.1/cinelerra/mwindow.h
cinelerra-5.1/cinelerra/mwindowedit.C
cinelerra-5.1/cinelerra/pluginset.C
cinelerra-5.1/cinelerra/savefile.C
cinelerra-5.1/cinelerra/setformat.C
cinelerra-5.1/cinelerra/setformat.h
cinelerra-5.1/cinelerra/trackcanvas.C
cinelerra-5.1/cinelerra/transitionpopup.C

index 102c803c8d943b36e1cbbed377021244b170d451..6b7173099b5b18d8ee08ff38714a1bb0126768e7 100644 (file)
@@ -80,7 +80,8 @@ void AssetPopup::create_objects()
        add_item(index = new AssetPopupBuildIndex(mwindow, this));
        add_item(view = new AssetPopupView(mwindow, this));
        add_item(view_window = new AssetPopupViewWindow(mwindow, this));
-       add_item(mixer = new AssetPopupMixer(mwindow, this));
+       add_item(open_mixer = new AssetPopupOpenMixer(mwindow, this));
+       add_item(insert_mixer = new AssetPopupInsertMixer(mwindow, this));
        add_item(new AssetPopupPaste(mwindow, this));
        add_item(menu_item = new BC_MenuItem(_("Match...")));
        menu_item->add_submenu(submenu = new BC_SubMenu());
@@ -269,25 +270,44 @@ int AssetPopupViewWindow::handle_event()
        return 1;
 }
 
-AssetPopupMixer::AssetPopupMixer(MWindow *mwindow, AssetPopup *popup)
+AssetPopupOpenMixer::AssetPopupOpenMixer(MWindow *mwindow, AssetPopup *popup)
  : BC_MenuItem(_("Open Mixers"))
 {
        this->mwindow = mwindow;
        this->popup = popup;
 }
 
-AssetPopupMixer::~AssetPopupMixer()
+AssetPopupOpenMixer::~AssetPopupOpenMixer()
 {
 }
 
-int AssetPopupMixer::handle_event()
+int AssetPopupOpenMixer::handle_event()
 {
-       mwindow->gui->lock_window("AssetPopupMixer::handle_event");
+       mwindow->gui->lock_window("AssetPopupOpenMixer::handle_event");
        mwindow->create_mixers();
        mwindow->gui->unlock_window();
        return 1;
 }
 
+AssetPopupInsertMixer::AssetPopupInsertMixer(MWindow *mwindow, AssetPopup *popup)
+ : BC_MenuItem(_("Insert Mixers"))
+{
+       this->mwindow = mwindow;
+       this->popup = popup;
+}
+
+AssetPopupInsertMixer::~AssetPopupInsertMixer()
+{
+}
+
+int AssetPopupInsertMixer::handle_event()
+{
+       mwindow->gui->lock_window("AssetPopupInsertMixer::handle_event");
+       mwindow->create_mixers(-1);
+       mwindow->gui->unlock_window();
+       return 1;
+}
+
 AssetPopupPaste::AssetPopupPaste(MWindow *mwindow, AssetPopup *popup)
  : BC_MenuItem(_("Paste"))
 {
index c30c155c5cfb50ad1a846a8ab0434c80719c7b5e..eea5a8eacb6019f2718ff0b75e5c2548ee73152c 100644 (file)
@@ -54,7 +54,8 @@ public:
        AssetPopupBuildIndex *index;
        AssetPopupView *view;
        AssetPopupViewWindow *view_window;
-       AssetPopupMixer *mixer;
+       AssetPopupOpenMixer *open_mixer;
+       AssetPopupInsertMixer *insert_mixer;
        AWindowListFormat *format;
 };
 
@@ -121,11 +122,23 @@ public:
        AssetPopup *popup;
 };
 
-class AssetPopupMixer : public BC_MenuItem
+class AssetPopupOpenMixer : public BC_MenuItem
 {
 public:
-       AssetPopupMixer(MWindow *mwindow, AssetPopup *popup);
-       ~AssetPopupMixer();
+       AssetPopupOpenMixer(MWindow *mwindow, AssetPopup *popup);
+       ~AssetPopupOpenMixer();
+
+       int handle_event();
+
+       MWindow *mwindow;
+       AssetPopup *popup;
+};
+
+class AssetPopupInsertMixer : public BC_MenuItem
+{
+public:
+       AssetPopupInsertMixer(MWindow *mwindow, AssetPopup *popup);
+       ~AssetPopupInsertMixer();
 
        int handle_event();
 
index aeeb5d3fd4b70a09f313cc05edcc6f46db9578ec..4cdb174c59728bd6b248c6ad297f22ff5706fa8a 100644 (file)
@@ -38,7 +38,8 @@ class AssetPopupSort;
 class AssetPopupBuildIndex;
 class AssetPopupView;
 class AssetPopupViewWindow;
-class AssetPopupMixer;
+class AssetPopupOpenMixer;
+class AssetPopupInsertMixer;
 class AssetPopupPaste;
 class AssetMatchSize;
 class AssetMatchRate;
index 9e3c2a5efcd63a0d52b3cf42df26935420d9fd72..ae7ab0c7d3ef8edf15119a71aeaa3728dc8a3e76 100644 (file)
@@ -326,6 +326,14 @@ int Edits::optimize()
                }
        }
 
+// trim edits before position 0
+       while( first && first->startproject+first->length < 0 )
+               delete first;
+       if( first && first->startproject < 0 ) {
+               first->length += first->startproject;
+               first->startproject = 0;
+       }
+
 // Insert silence between edits which aren't consecutive
        for(current = last; current; current = current->previous)
        {
index 8312c71ec09aa09471e7a7f019f0d2e0575bda84..cc2e84fb3c7b128ab5ecb5716f0c8bb310979fd5 100644 (file)
@@ -38,6 +38,8 @@
 #include "floatauto.h"
 #include "floatautos.h"
 #include "guicast.h"
+#include "keyframe.h"
+#include "keyframes.h"
 #include "indexstate.h"
 #include "interlacemodes.h"
 #include "labels.h"
@@ -1582,6 +1584,21 @@ double EDL::prev_edit(double position)
        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;
@@ -1800,3 +1817,314 @@ int EDL::regroup(int 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->copy_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->position<clip_position )
+                                               clip_label = clip_label->next;
+                                       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; i<AUTOMATION_TOTAL; ++i ) {
+                                       Autos *autos = automation->autos[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; i<track->plugin_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->copy_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; i<AUTOMATION_TOTAL; ++i ) {
+                                               Autos *autos = track->automation->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; i<track->plugin_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; i<AUTOMATION_TOTAL; ++i ) {
+                               Autos *src_autos = src->automation->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; i<src->plugin_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);
+                                       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);
+}
+
index f355b57ab21b29fdd24acb1b3bbe4fa53738954b..0798b408164c6889a2b626ec30b266e98d03cb10 100644 (file)
@@ -167,6 +167,7 @@ public:
 // return next/prev edit starting from position
        double next_edit(double position);
        double prev_edit(double position);
+       double skip_silence(double position);
 // Debug
        int dump(FILE *fp=stdout);
        static int next_id();
@@ -221,6 +222,14 @@ public:
        int insert_clips(ArrayList<EDL*> *new_edls, int load_mode, Track *first_track = 0);
 // Add a copy of EDL* to the clip array.  Returns the copy.
        EDL* add_clip(EDL *edl);
+       EDL *selected_edits_to_clip(int packed,
+               double *start_position, Track **start_track,
+               int edit_labels, int edit_autos, int edit_plugins);
+       EDL *selected_edits_to_clip(int packed, double *start_position, Track **start_track);
+       void selected_edits_to_clipboard(int packed);
+       void paste_edits(EDL *clip, Track *first_track, double position, int overwrite,
+               int edit_edits, int edit_labels, int edit_autos, int edit_plugins);
+       void paste_edits(EDL *clip, Track *first_track, double position, int overwrite);
 // resequence group ids starting at next_id
        int regroup(int next_id);
 
index 58a83e9eafe7024f5b4c843c132b020e9d407706..e045a5321b27cec19568cca901113662ebb0d181 100644 (file)
@@ -264,7 +264,7 @@ int XMLTag::write_tag(FileXML *xml)
                const char *prop = properties[i]->prop;
                const char *value = properties[i]->value;
                int plen = strlen(prop), vlen = strlen(value);
-               bool need_quotes = !vlen || strchr(value,' ');
+               bool need_quotes = !vlen || strchr(value,' ') || strchr(value,'\n');
                buf->next(' ');
                xml->append_text(prop, plen);
                buf->next('=');
index 188782c1a09e3c69c8d85bb821c7c9ff4ff58240..1e91b6ca5e6cfdf1563809add3b13a2c32875d0d 100644 (file)
 #include "transportque.h"
 #include "zwindow.h"
 
+// c = corr(a,b): A=fft(a),B=fft(b) C=A*conj(B) c=ifft(C)
+static inline void conj_product(int n, double *rp, double *ip,
+               double *arp, double *aip, double *brp, double *bip)
+{
+       for( int i=0; i<n; ++i ) {
+               double ar = arp[i], ai = aip[i];
+               double br = brp[i], bi = -bip[i];
+               rp[i] = ar*br - ai*bi;
+               ip[i] = ar*bi + ai*br;
+       }
+}
+
 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
 {
        this->mixer = mix;
        this->nudge = 0;
        mx = 0;  mi = -1;
+       br = 0;  bi = 0;
        aidx = -1;
 }
+MixersAlignMixer::~MixersAlignMixer()
+{
+       delete [] br;
+       delete [] bi;
+}
 
 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
        N_("Mixer"), N_("Nudge"),
@@ -205,8 +223,8 @@ void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
        cols[MTK_NO].append(new BC_ListBoxItem(no_text));
        char mixer_text[BCSTRLEN];
        Track *track = mtrk->track;
-       int midx = -1, m = dialog->mixer_of(track, midx);
-       snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,midx);
+       int k = -1, m = dialog->mixer_of(track, k);
+       snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,k);
        cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
        cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
 }
@@ -230,9 +248,14 @@ MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
        this->track = trk;
        this->no = no;
        this->nudge = 0;
+       this->ss = 0;
        mx = 0;  mi = -1;
 }
 
+MixersAlignATrack::~MixersAlignATrack()
+{
+}
+
 
 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
        N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
@@ -270,7 +293,7 @@ void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
 {
        char atrack_text[BCSTRLEN];
        Track *track = atrack->track;
-       int midx = -1, m = dialog->mixer_of(track, midx);
+       int m = dialog->mixer_of(track);
        snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
        cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
        cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
@@ -379,16 +402,28 @@ MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
 int MixersAlignMatch::handle_event()
 {
        if( !dialog->thread->running() ) {
-               gui->reset->disable();
-               gui->match->disable();
-               gui->apply->disable();
-               gui->undo->disable();
-               dialog->thread->start();
+               dialog->thread->start(1);
        }
        return 1;
 }
 
-MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
+MixersAlignMatchAll::MixersAlignMatchAll(MixersAlignWindow *gui,
+               MixersAlign *dialog, int x, int y)
+ : BC_GenericButton(x, y, _("Match All"))
+{
+       this->gui = gui;
+       this->dialog = dialog;
+}
+
+int MixersAlignMatchAll::handle_event()
+{
+       if( !dialog->thread->running() ) {
+               dialog->thread->start(0);
+       }
+       return 1;
+}
+
+MixersAlignNudgeTracks::MixersAlignNudgeTracks(MixersAlignWindow *gui,
                MixersAlign *dialog, int x, int y)
  : BC_GenericButton(x, y, _("Apply"))
 {
@@ -396,35 +431,91 @@ MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
        this->dialog = dialog;
 }
 
-int MixersAlignApply::calculate_width(BC_WindowBase *gui)
+int MixersAlignNudgeTracks::calculate_width(BC_WindowBase *gui)
 {
        return BC_GenericButton::calculate_w(gui, _("Apply"));
 }
 
-int MixersAlignApply::handle_event()
+int MixersAlignNudgeTracks::handle_event()
+{
+       dialog->nudge_tracks();
+       return 1;
+}
+
+MixersAlignNudgeSelected::MixersAlignNudgeSelected(MixersAlignWindow *gui,
+               MixersAlign *dialog, int x, int y)
+ : BC_GenericButton(x, y, _("Move"))
+{
+       this->gui = gui;
+       this->dialog = dialog;
+}
+
+int MixersAlignNudgeSelected::calculate_width(BC_WindowBase *gui)
+{
+       return BC_GenericButton::calculate_w(gui, _("Move"));
+}
+
+int MixersAlignNudgeSelected::handle_event()
 {
-       dialog->apply();
+       dialog->nudge_selected();
+       return 1;
+}
+
+MixersAlignUndoItem::MixersAlignUndoItem(const char *text, int no)
+ : BC_MenuItem(text)
+{
+       this->no = no;
+}
+MixersAlignUndoItem::~MixersAlignUndoItem()
+{
+}
+
+int MixersAlignUndoItem::handle_event()
+{
+       MixersAlignUndo *undo = (MixersAlignUndo *)get_popup_menu();
+       undo->dialog->apply_undo(no);
        return 1;
 }
 
 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
                MixersAlign *dialog, int x, int y)
- : BC_GenericButton(x, y, _("Undo"))
+ : BC_PopupMenu(x, y, 100, _("Undo"))
 {
        this->gui = gui;
        this->dialog = dialog;
 }
+MixersAlignUndo::~MixersAlignUndo()
+{
+}
 
-int MixersAlignUndo::handle_event()
+void MixersAlignUndo::create_objects()
 {
-       MWindow *mwindow = dialog->mwindow;
-       mwindow->edl->copy_all(dialog->undo_edl);
-       mwindow->gui->lock_window("MixersAlignUndo::handle_event");
-       mwindow->update_gui(1);
-       mwindow->gui->unlock_window();
-       return gui->reset->handle_event();
+       add_undo_item(0);
+}
+
+void MixersAlignUndo::add_undo_item(int no)
+{
+       char text[BCSTRLEN];
+       if( no > 0 )
+               sprintf(text, _("chkpt %d"), no);
+       else
+               sprintf(text, _("start over"));
+       add_item(new MixersAlignUndoItem(text, no));
+}
+
+MixersAlignCheckPoint::MixersAlignCheckPoint(MixersAlignWindow *gui,
+               MixersAlign *dialog, int x, int y)
+ : BC_GenericButton(x, y, 100, _("CheckPoint"))
+{
+       this->gui = gui;
+       this->dialog = dialog;
 }
 
+int MixersAlignCheckPoint::handle_event()
+{
+       dialog->check_point();
+       return 1;
+}
 
 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
  : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
@@ -446,7 +537,7 @@ void MixersAlignWindow::create_objects()
        atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
        add_subwindow(atrack_title);
        y += mixer_title->get_h() + 10;
-       int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 15;
+       int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 32;
        int lh = y2 - y1;
        mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
        add_subwindow(mixer_list);
@@ -455,11 +546,18 @@ void MixersAlignWindow::create_objects()
        atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
        add_subwindow(atrack_list);
        int xr = x2-10 - MixersAlignReset::calculate_width(this);
-       add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y2+20));
-       add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y2+20));
-       int xa = x3-10 - MixersAlignApply::calculate_width(this);
-       add_subwindow(apply = new MixersAlignApply(this, dialog, xa, y2+20));
-       add_subwindow(undo = new MixersAlignUndo(this, dialog, x3+10, y2+20));
+       y1 = y2+20;
+       add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y1));
+       add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y1));
+       int xa = x3-10 - MixersAlignNudgeTracks::calculate_width(this);
+       add_subwindow(nudge_tracks = new MixersAlignNudgeTracks(this, dialog, xa, y1));
+       y2 = y1 + nudge_tracks->get_h() + 10;
+       add_subwindow(match_all = new MixersAlignMatchAll(this, dialog, xr, y2));
+       add_subwindow(nudge_selected = new MixersAlignNudgeSelected(this, dialog, xa, y2));
+       int xu = x3+10;
+       add_subwindow(check_point = new MixersAlignCheckPoint(this, dialog, xu, y1));
+       add_subwindow(undo = new MixersAlignUndo(this, dialog, xu, y2));
+       undo->create_objects();
 
        add_subwindow(new BC_OKButton(this));
        add_subwindow(new BC_CancelButton(this));
@@ -473,17 +571,23 @@ int MixersAlignWindow::resize_event(int w, int h)
        mtrack_title->reposition_window(x2, y);
        atrack_title->reposition_window(x3, y);
        y += mixer_title->get_h() + 10;
-       int y1 = y, y2 = h - BC_OKButton::calculate_h() - 15;
+       int y1 = y, y2 = h - BC_OKButton::calculate_h() - 32;
        int lh = y2 - y1;
        mixer_list->reposition_window(x1, y, x2-x1-20, lh);
        mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
        atrack_list->reposition_window(x3, y, x4-x3-20, lh);
        int xr = x2-10 - MixersAlignReset::calculate_width(this);
-       reset->reposition_window(xr, y2+20);
-       match->reposition_window(x2+10, y2+20);
-       int xa = x3-10 - MixersAlignApply::calculate_width(this);
-       apply->reposition_window(xa, y2+20);
-       undo->reposition_window(x3+10, y2+20);
+       y1 = y2+20;
+       reset->reposition_window(xr, y1);
+       match->reposition_window(x2+10, y1);
+       int xa = x3-10 - MixersAlignNudgeTracks::calculate_width(this);
+       nudge_tracks->reposition_window(xa, y1);
+       y2 = y1 + nudge_tracks->get_h() + 10;
+       match_all->reposition_window(xr, y2);
+       nudge_selected->reposition_window(xa, y2);
+       int xu = x3+10;
+       check_point->reposition_window(xu, y1);
+       undo->reposition_window(xu, y2);
        return 0;
 }
 
@@ -532,9 +636,9 @@ MixersAlign::MixersAlign(MWindow *mwindow)
        master_r = 0;
        master_i = 0;
        master_len = 0;
+       sample_len = 0x10000;
        progress = 0;
        failed = 0;
-       undo_edl = 0;
        master_start = master_end = 0;
        audio_start = audio_end = 0;
 }
@@ -556,9 +660,10 @@ void MixersAlign::start_dialog(int wx, int wy)
 {
        this->wx = wx;
        this->wy = wy;
-       this->undo_edl = new EDL();
-       undo_edl->create_objects();
-       undo_edl->copy_all(mwindow->edl);
+       EDL *start_over = new EDL();
+       start_over->create_objects();
+       start_over->copy_all(mwindow->edl);
+       undo_edls.append(start_over);
        start();
 }
 
@@ -577,14 +682,15 @@ BC_Window *MixersAlign::new_gui()
        return ma_gui;
 }
 
-void MixersAlign::apply()
+// shift armed mixer tracks by nudge
+void MixersAlign::nudge_tracks()
 {
        int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
-       int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
+       int midx = mmixer_of(idx);
        EDL *edl = mwindow->edl;
        
        for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
-               if( m == mid ) continue;  // master does not move
+               if( m == midx ) continue;  // master does not move
                MixersAlignMixer *mix = mixers[m];
                Mixer *mixer = mix->mixer;
                for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
@@ -595,14 +701,13 @@ void MixersAlign::apply()
                        double nudge = mix->nudge;
                        int record = track->record;  track->record = 1;
                        if( nudge < 0 ) {
-                               edl->clear(0, -nudge,
+                               track->clear(0, -nudge, 1,
                                        edl->session->labels_follow_edits,
                                        edl->session->plugins_follow_edits,
-                                       edl->session->autos_follow_edits);
+                                       edl->session->autos_follow_edits, 0);
                        }
                        else if( nudge > 0 ) {
-                               edl->paste_silence(0, nudge,
-                                       edl->session->labels_follow_edits,
+                               track->paste_silence(0, nudge,
                                        edl->session->plugins_follow_edits,
                                        edl->session->autos_follow_edits);
                        }
@@ -611,9 +716,93 @@ void MixersAlign::apply()
        }
        edl->optimize();
 
-       mwindow->gui->lock_window("MixersAlign::handle_done_event");
+       mwindow->gui->lock_window("MixersAlign::apply_tracks");
+       mwindow->update_gui(1);
+       mwindow->gui->unlock_window();
+       clear_mixer_nudge();
+}
+
+// move selected mixer edits by nudge
+void MixersAlign::nudge_selected()
+{
+       int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
+       int midx = mmixer_of(idx);
+       EDL *edl = mwindow->edl;
+
+       ArrayList<int> track_arms;  // ugly
+       for( Track *track=edl->tracks->first; track; track=track->next ) {
+               track_arms.append(track->record);
+               track->record = 0;
+       }
+       for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
+               if( m == midx ) continue;  // master does not move
+               MixersAlignMixer *mix = mixers[m];
+               Mixer *mixer = mix->mixer;
+               for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
+                       int id = mixer->mixer_ids[i];
+                       Track *track = edl->tracks->first;
+                       while( track && track->mixer_id != id ) track = track->next;
+                       if( !track ) continue;
+                       double nudge = mix->nudge;
+                       track->record = 1;
+                       double position = 0;  Track *first_track = 0;
+                       EDL *clip = edl->selected_edits_to_clip(0, &position, &first_track);
+                       if( clip ) {
+                               Track *clip_track = clip->tracks->first;
+                               Track *edl_track = first_track;
+                               while( clip_track && edl_track ) {
+                                       Edit *edit = clip_track->edits->first;
+                                       for( ; edit; edit=edit->next ) {
+                                               double start = clip_track->from_units(edit->startproject);
+                                               double end = clip_track->from_units(edit->startproject+edit->length);
+                                               start += position;  end += position;
+                                               edl_track->clear(start, end, 1,
+                                                       edl->session->labels_follow_edits,
+                                                       edl->session->plugins_follow_edits,
+                                                       edl->session->autos_follow_edits, 0);
+                                               edl_track->paste_silence(start, end,
+                                                       edl->session->plugins_follow_edits,
+                                                       edl->session->autos_follow_edits);
+                                       }
+                                       clip_track = clip_track->next;
+                                       edl_track = edl_track->next;
+                               }
+                               position += nudge;
+                               edl->paste_edits(clip, first_track, position, 1);
+                       }
+                       track->record = 0;
+               }
+       }
+       int i = 0;
+       for( Track *track=edl->tracks->first; track; track=track->next )
+               track->record = track_arms[i++];
+       edl->optimize();
+
+       mwindow->gui->lock_window("MixersAlign::apply_selected");
        mwindow->update_gui(1);
        mwindow->gui->unlock_window();
+       clear_mixer_nudge();
+}
+
+
+void MixersAlign::clear_mixer_nudge()
+{
+// so pressing apply twice does not damage the result
+       for( int m=0; m<mixers.size(); ++m )
+               mixers[m]->nudge = 0;
+       ma_gui->mixer_list->load_list();
+       ma_gui->lock_window("MixersAlign::clear_mixer_nudge");
+       ma_gui->mixer_list->update();
+       ma_gui->unlock_window();
+}
+
+void MixersAlign::check_point()
+{
+       ma_gui->undo->add_undo_item(undo_edls.size());
+       EDL *undo_edl = new EDL();
+       undo_edl->create_objects();
+       undo_edl->copy_all(mwindow->edl);
+       undo_edls.append(undo_edl);
 }
 
 
@@ -627,16 +816,36 @@ MixersAlignThread::~MixersAlignThread()
        join();
 }
 
+void MixersAlignThread::start(int fwd)
+{
+       this->fwd = fwd;
+       MixersAlignWindow *gui = dialog->ma_gui;
+       gui->reset->disable();
+       gui->match->disable();
+       gui->match_all->disable();
+       gui->nudge_tracks->disable();
+       gui->nudge_selected->disable();
+       gui->check_point->disable();
+//     gui->undo->disable();
+       Thread::start();
+}
+
 void MixersAlignThread::run()
 {
-       dialog->update_match();
-       MixersAlignWindow *ma_gui = dialog->ma_gui;
-       ma_gui->lock_window("MixersAlignThread::run");
-       ma_gui->reset->enable();
-       ma_gui->match->enable();
-       ma_gui->apply->enable();
-       ma_gui->undo->enable();
-       ma_gui->unlock_window();
+       if( fwd )
+               dialog->match_fwd();
+       else
+               dialog->match_rev();
+       MixersAlignWindow *gui = dialog->ma_gui;
+       gui->lock_window("MixersAlignThread::run");
+       gui->reset->enable();
+       gui->match->enable();
+       gui->match_all->enable();
+       gui->nudge_tracks->enable();
+       gui->nudge_selected->enable();
+       gui->check_point->enable();
+//     gui->undo->enable();
+       gui->unlock_window();
 }
 
 
@@ -645,11 +854,10 @@ void MixersAlign::handle_done_event(int result)
        if( thread->running() ) {
                failed = -1;
                thread->join();
-               return;
        }
        if( !result ) {
                EDL *edl = mwindow->edl;
-               mwindow->edl = undo_edl;
+               mwindow->edl = undo_edls[0];
                mwindow->undo_before();
                mwindow->edl = edl;
                mwindow->undo_after(_("align mixers"), LOAD_ALL);
@@ -658,8 +866,6 @@ void MixersAlign::handle_done_event(int result)
 
 void MixersAlign::handle_close_event(int result)
 {
-       undo_edl->remove_user();
-       undo_edl = 0;
        ma_gui = 0;
 }
 
@@ -693,6 +899,19 @@ void MixersAlign::load_atracks()
 }
 
 
+int MixersAlign::atrack_of(MixersAlignMixer *mix, int ch)
+{
+       int k = -1;
+       Mixer *mixer = mix->mixer;
+       for( int i=0,n=mixer->mixer_ids.size(); i<n; ++i ) {
+               int id = mixer->mixer_ids[i];  k = atracks.size();
+               while( --k >= 0 && atracks[k]->track->mixer_id != id );
+               if( k < 0 ) continue;
+               if( --ch < 0 ) break;
+       }
+       return k;
+}
+
 int MixersAlign::mixer_of(Track *track, int &midx)
 {
        int id = track->mixer_id, k = mixers.size(), idx = -1;
@@ -702,6 +921,7 @@ int MixersAlign::mixer_of(Track *track, int &midx)
        return k;
 }
 
+
 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
 {
        EDL *edl = new EDL(mwindow->edl);
@@ -736,12 +956,13 @@ EDL *MixersAlign::mixer_master_clip(Track *track)
        return edl;
 }
 
-int64_t MixersAlign::mixer_tracks_total()
+int64_t MixersAlign::mixer_tracks_total(int midx)
 {
        int64_t total_len = 0;
        int64_t sample_rate = mwindow->edl->get_sample_rate();
-       int m = -1, idx = 0;
-       for( ; (m=ma_gui->mixer_list->get_selection_number(0, idx))>=0 ; ++idx ) {
+       int m = -1;
+       for( int i=0; (m=ma_gui->mixer_list->get_selection_number(0, i))>=0 ; ++i ) {
+               if( m == midx ) continue;
                Mixer *mixer = mixers[m]->mixer;
                double render_end = 0;
                Track *track = mwindow->edl->tracks->first;
@@ -759,6 +980,20 @@ int64_t MixersAlign::mixer_tracks_total()
        return total_len;
 }
 
+void MixersAlign::apply_undo(int no)
+{
+       if( thread->running() ) {
+               failed = -1;
+               thread->join();
+       }
+       EDL *undo_edl = undo_edls[no];
+       mwindow->edl->copy_all(undo_edl);
+       mwindow->gui->lock_window("MixersAlignUndo::handle_event");
+       mwindow->update_gui(1);
+       mwindow->gui->unlock_window();
+       ma_gui->reset->handle_event();
+}
+
 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
  : RenderEngine(0, mwindow->preferences, 0, 0)
 {
@@ -781,89 +1016,240 @@ int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
        return arender ? arender->process_buffer(samples, len, pos) : -1;
 }
 
-MixersAlignPackage::MixersAlignPackage()
+// scan mixer tracks for best target
+MixersAlignScanFarm::MixersAlignScanFarm(MixersAlign *dialog, int cpus, int n)
+ : LoadServer(cpus, n)
+{
+       dialog->farming->lock("MixersAlignScanFarm::MixersAlignScanFarm");
+       this->dialog = dialog;
+       this->len = len;
+}
+MixersAlignScanFarm::~MixersAlignScanFarm()
+{
+       dialog->farming->unlock();
+}
+
+MixersAlignScanPackage::MixersAlignScanPackage(MixersAlignScanFarm *farm)
 {
        mixer = 0;
 }
+MixersAlignScanPackage::~MixersAlignScanPackage()
+{
+}
 
-MixersAlignPackage::~MixersAlignPackage()
+LoadPackage* MixersAlignScanFarm::new_package()
 {
+       return new MixersAlignScanPackage(this);
 }
 
-MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
- : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
+void MixersAlignScanFarm::init_packages()
 {
-       this->dialog = dialog;
-       dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
+       int idx = dialog->ma_gui->mtrack_list->get_selection_number(0, 0);
+       int midx = dialog->mmixer_of(idx);
+       for( int i=0, k=0; i<get_total_packages(); ++k ) {
+               int m = dialog->ma_gui->mixer_list->get_selection_number(0, k);
+               if( m == midx ) continue;
+               MixersAlignScanPackage *pkg = (MixersAlignScanPackage *)get_package(i++);
+               pkg->mixer = dialog->mixers[m];
+       }
 }
-MixersAlignFarm::~MixersAlignFarm()
+
+MixersAlignScanClient::MixersAlignScanClient(MixersAlignScanFarm *farm)
+ : LoadClient(farm)
 {
-       dialog->farming->unlock();
+       pos = -1;
+       len1 = 0;
 }
 
-LoadPackage* MixersAlignFarm::new_package()
+MixersAlignScanClient::~MixersAlignScanClient()
 {
-       return new MixersAlignPackage();
 }
 
-void MixersAlignFarm::init_packages()
+LoadClient* MixersAlignScanFarm::new_client()
 {
-       for( int i = 0; i < get_total_packages(); ++i ) {
-               int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
-               MixersAlignPackage *package = (MixersAlignPackage *)get_package(i);
-               package->mixer = dialog->mixers[m];
+       return new MixersAlignScanClient(this);
+}
+
+void MixersAlignScanClient::process_package(LoadPackage *package)
+{
+       MixersAlignScanFarm *farm = (MixersAlignScanFarm *)server;
+       MixersAlign *dialog = farm->dialog;
+       if( dialog->progress->is_cancelled() ) dialog->failed = -1;
+       if( dialog->failed ) return;
+       pkg = (MixersAlignScanPackage *)package;
+       MixersAlignMixer *mix = pkg->mixer;
+
+       EDL *edl = dialog->mixer_audio_clip(mix->mixer);
+       MixersAlignARender audio(dialog->mwindow, edl);
+       double start = edl->skip_silence(0);
+       int channels = edl->get_audio_channels();
+       int64_t sample_rate = edl->get_sample_rate();
+       int64_t cur_pos = start * sample_rate;
+       int64_t end_pos = edl->get_audio_samples();
+       int len = dialog->sample_len, len2 = len/2;
+       cur_pos &= ~(len2-1);
+       if( cur_pos ) dialog->update_progress(cur_pos);
+
+       int ret = 0;
+       Samples *samples[MAX_CHANNELS];
+       for( int i=0; i<MAX_CHANNELS; ++i )
+               samples[i] = i<channels ? new Samples(len2) : 0;
+       int cpus = bmin(dialog->mwindow->preferences->processors, channels);
+       MixersAlignTarget targ(channels, cpus, this, samples, len2);
+
+       while( !ret && !dialog->failed && cur_pos < end_pos ) {
+               pos = cur_pos;
+               int64_t nxt_pos = pos + len2;
+               if( nxt_pos > end_pos ) nxt_pos = end_pos;
+               len1 = nxt_pos - cur_pos;
+               ret = audio.render(samples, len1, pos);
+               if( ret ) break;
+               targ.process_packages();
+               dialog->update_progress(len1);
+               if( dialog->progress->is_cancelled() ) dialog->failed = -1;
+               cur_pos = nxt_pos;
+       }
+
+       if( !ret && !dialog->failed ) {
+               int idx = -1;
+               double sd2 = 0;
+               MixersAlignMixer *mix = pkg->mixer;
+               MixersAlignTargetPackage *best_pkg = 0;
+               for( int i=0,n=targ.get_total_packages(); i<n; ++i ) {
+                       MixersAlignTargetPackage *targ_pkg =
+                               (MixersAlignTargetPackage *) targ.get_package(i);
+                       if( sd2 >= targ_pkg->sd2 ) continue;
+                       sd2 = targ_pkg->sd2;
+                       int k = dialog->atrack_of(mix, i);
+                       if( k < 0 ) continue;
+                       MixersAlignATrack *atrk = dialog->atracks[k];
+                       atrk->mi = targ_pkg->pos;
+                       atrk->ss = targ_pkg->ss;
+                       idx = k;  best_pkg = targ_pkg;
+               }
+               if( idx >= 0 ) {
+                       mix->br = new double[len];
+                       mix->bi = new double[len];
+                       double *br = mix->br;
+                       double *bp = best_pkg->best;
+                       int i = 0;
+                       while( i < len2 ) br[i++] = *bp++;
+                       while( i < len ) br[i++] = 0;
+                       FFT fft;
+                       fft.do_fft(len, 0, mix->br, 0, mix->br, mix->bi);
+                       mix->aidx = idx;
+               }
+       }
+
+       if( ret && !dialog->failed ) {
+               eprintf("Audio render failed:\n%s", edl->path);
+               dialog->failed = 1;
        }
+
+       for( int i=channels; --i>=0; ) delete samples[i];
+       edl->remove_user();
+}
+
+// scan mixer channels for best target
+MixersAlignTarget::MixersAlignTarget(int n, int cpus,
+               MixersAlignScanClient *scan, Samples **samples, int len)
+ : LoadServer(n, cpus)
+{
+       this->scan = scan;
+       this->samples = samples;
+       this->len = len;
+}
+MixersAlignTarget::~MixersAlignTarget()
+{
 }
 
-LoadClient* MixersAlignFarm::new_client()
+MixersAlignTargetClient::MixersAlignTargetClient()
+ : LoadClient()
+{
+}
+MixersAlignTargetClient::~MixersAlignTargetClient()
 {
-       return new MixersAlignClient(this);
 }
 
-MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
- : LoadClient(farm)
+LoadClient* MixersAlignTarget::new_client()
 {
+       return new MixersAlignTargetClient();
 }
 
-MixersAlignClient::~MixersAlignClient()
+MixersAlignTargetPackage::MixersAlignTargetPackage(MixersAlignTarget *targ)
 {
+       ss = 0;  sd2 = 0;
+       pos = -1;
+       best = new double[targ->len];
+}
+MixersAlignTargetPackage::~MixersAlignTargetPackage()
+{
+       delete [] best;
 }
 
-void MixersAlignClient::process_package(LoadPackage *pkg)
+LoadPackage* MixersAlignTarget::new_package()
 {
-       MixersAlignFarm *farm = (MixersAlignFarm *)server;
-       MixersAlign *dialog = farm->dialog;
-       MixersAlignPackage *package = (MixersAlignPackage *)pkg;
-       dialog->process_package(farm, package);
+       return new MixersAlignTargetPackage(this);
 }
 
-static inline void conj_product(int n, double *rp, double *ip,
-               double *arp, double *aip, double *brp, double *bip)
+void MixersAlignTarget::init_packages()
 {
-       for( int i=0; i<n; ++i ) {
-               double ar = arp[i], ai = aip[i];
-               double br = brp[i], bi = -bip[i];
-               rp[i] = ar*br - ai*bi;
-               ip[i] = ar*bi + ai*br;
-       }
 }
 
-void MixersAlign::process_package(MixersAlignFarm *farm,
-                MixersAlignPackage *package)
+void MixersAlignTargetClient::process_package(LoadPackage *package)
 {
-       if( progress->is_cancelled() ) failed = -1;
-       if( failed ) return;
-       MixersAlignMixer *amix = package->mixer;
-       EDL *edl = mixer_audio_clip(amix->mixer);
+       MixersAlignTarget *targ = (MixersAlignTarget *)server;
+       MixersAlignScanClient *scan = targ->scan;
+       MixersAlignScanFarm *farm = (MixersAlignScanFarm *)scan->server;
+       MixersAlign *dialog = farm->dialog;
+       if( dialog->progress->is_cancelled() ) dialog->failed = -1;
+       if( dialog->failed ) return;
+       pkg = (MixersAlignTargetPackage *)package;
+
+       int ch = get_package_number();
+       double *data = targ->samples[ch]->get_data();
+       int len1 = scan->len1;
+// computes sum(s**2), sum(d2**2) d2=discrete 2nd deriv
+// d0=s[i+0]-s[i+1], d1=s[i+1]-s[i+2], d2=d0-d1
+// d = s[i+0] - 2*s[i+1] + s[i+2]
+       double ss = 0, sd2 = 0;
+       double a = 0, b = 0, c = 0;
+       for( int i=0; i<len1; ++i ) {
+               a = b;  b = c;  c = data[i];
+               double d = a - 2*b + c;
+               ss += c*c;  sd2 += d*d;
+       }
+//best is highest sd2
+       if( pkg->sd2 < sd2 ) {
+               pkg->sd2 = sd2;
+               pkg->ss = ss;
+               pkg->pos = scan->pos;
+printf("targ %s:%d at %jd,ss=%f sd2=%f\n",
+  scan->pkg->mixer->mixer->title, ch, scan->pos, ss, sd2);
+               double *best = pkg->best;
+               int i = 0, len = targ->len;
+               while( i < len1 ) best[i++] = *data++;
+               while( i < len ) best[i++] = 0;
+       }
+}
 
+void MixersAlign::scan_master(Track *track)
+{
+       EDL *edl = mixer_master_clip(track);
        MixersAlignARender audio(mwindow, edl);
-       int sample_rate = edl->get_sample_rate();
+
        int channels = edl->get_audio_channels();
+       int64_t sample_rate = edl->get_sample_rate();
        int64_t audio_samples = edl->get_audio_samples();
        int64_t cur_pos = audio_start * sample_rate;
        int64_t end_pos = audio_end * sample_rate;
        if( end_pos > audio_samples ) end_pos = audio_samples;
-       int len = master_len, len2 = len/2;
+       if( cur_pos >= end_pos ) {
+               eprintf(_("scan master track empty"));
+               failed = 1;
+               return;
+       }
+       int len = sample_len, len2 = len/2;
        double *audio_r = new double[len];
        double *audio_i = new double[len];
        Samples *samples[2][MAX_CHANNELS];
@@ -871,23 +1257,155 @@ void MixersAlign::process_package(MixersAlignFarm *farm,
                for( int i=0; i<MAX_CHANNELS; ++i )
                        samples[k][i] = i<channels ? new Samples(len2) : 0;
        }
+
+       int m = 0;
+       for( int i=0,n=mixers.size(); i<n; ++i )
+               if( mixers[i]->br ) ++m;
+       int cpus = bmin(mwindow->preferences->processors, m);
+       MixersAlignMatchRevFarm farm(m, cpus, this, audio_r, audio_i, len);
+
+       FFT fft;
+       Timer timer;
+       start_progress(end_pos - cur_pos);
+
        int k = 0;
-       int64_t nxt_pos = cur_pos+len2;
+       int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
        if( nxt_pos > end_pos ) nxt_pos = end_pos;
-       int len1 = nxt_pos - cur_pos;
-       int ret = audio.render(samples[k], len1, cur_pos);
+       int len1 = nxt_pos - pos;
+       int ret = audio.render(samples[k], len1, pos);
        while( !ret && !failed && cur_pos < end_pos ) {
-               update_progress(len1);
-               int64_t pos = cur_pos;
+               pos = cur_pos;
                cur_pos = nxt_pos;  nxt_pos += len2;
                if( nxt_pos > end_pos ) nxt_pos = end_pos;
                len1 = nxt_pos - cur_pos;
                ret = audio.render(samples[1-k], len1, cur_pos);
                if( ret ) break;
+               update_progress(len2);
+               int i = 0;
+               double *lp = samples[k][0]->get_data();
+               for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
+               double *np = samples[k=1-k][0]->get_data();
+               for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
+               while( i < len ) audio_r[i++] = 0;
+               fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
+               farm.pos = pos;
+               farm.process_packages();
+               if( progress->is_cancelled() ) failed = -1;
+       }
+
+       if( ret && !failed ) {
+               eprintf("Audio render failed:\n%s", edl->path);
+               failed = 1;
+       }
+
+       char text[BCSTRLEN];
+       double secs = timer.get_difference()/1000.;
+       sprintf(text, _("Match mixer done: %0.3f secs"), secs);
+       stop_progress(text);
+
+       delete [] audio_r;
+       delete [] audio_i;
+       for( int k=0; k<2; ++k ) {
+               for( int i=channels; --i>=0; )
+                       delete samples[k][i];
+       }
+       edl->remove_user();
+}
+
+MixersAlignMatchFwdPackage::MixersAlignMatchFwdPackage()
+{
+       mixer = 0;
+}
+
+MixersAlignMatchFwdPackage::~MixersAlignMatchFwdPackage()
+{
+}
+
+MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm(MixersAlign *dialog, int n)
+ : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
+{
+       this->dialog = dialog;
+       dialog->farming->lock("MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm");
+}
+MixersAlignMatchFwdFarm::~MixersAlignMatchFwdFarm()
+{
+       dialog->farming->unlock();
+}
+
+LoadPackage* MixersAlignMatchFwdFarm::new_package()
+{
+       return new MixersAlignMatchFwdPackage();
+}
+
+void MixersAlignMatchFwdFarm::init_packages()
+{
+       for( int i = 0; i < get_total_packages(); ++i ) {
+               int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
+               MixersAlignMatchFwdPackage *package = (MixersAlignMatchFwdPackage *)get_package(i);
+               package->mixer = dialog->mixers[m];
+       }
+}
+
+LoadClient* MixersAlignMatchFwdFarm::new_client()
+{
+       return new MixersAlignMatchFwdClient(this);
+}
+
+MixersAlignMatchFwdClient::MixersAlignMatchFwdClient(MixersAlignMatchFwdFarm *farm)
+ : LoadClient(farm)
+{
+}
+
+MixersAlignMatchFwdClient::~MixersAlignMatchFwdClient()
+{
+}
+
+void MixersAlignMatchFwdClient::process_package(LoadPackage *package)
+{
+       MixersAlignMatchFwdFarm *farm = (MixersAlignMatchFwdFarm *)server;
+       MixersAlign *dialog = farm->dialog;
+       if( dialog->progress->is_cancelled() ) dialog->failed = -1;
+       if( dialog->failed ) return;
+       pkg = (MixersAlignMatchFwdPackage *)package;
+
+       MixersAlignMixer *amix = pkg->mixer;
+       EDL *edl = dialog->mixer_audio_clip(amix->mixer);
+       MixersAlignARender audio(dialog->mwindow, edl);
+       int channels = edl->get_audio_channels();
+       int64_t sample_rate = edl->get_sample_rate();
+       int64_t audio_samples = edl->get_audio_samples();
+       int64_t cur_pos = dialog->audio_start * sample_rate;
+       int64_t end_pos = dialog->audio_end * sample_rate;
+       if( end_pos > audio_samples ) end_pos = audio_samples;
+       int len = dialog->master_len, len2 = len/2;
+       double *audio_r = new double[len];
+       double *audio_i = new double[len];
+
+       Samples *samples[2][MAX_CHANNELS];
+       for( int k=0; k<2; ++k ) {
+               for( int i=0; i<MAX_CHANNELS; ++i )
+                       samples[k][i] = i<channels ? new Samples(len2) : 0;
+       }
+
+       FFT fft;
+       int k = 0;
+       int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
+       if( nxt_pos > end_pos ) nxt_pos = end_pos;
+       int len1 = nxt_pos - pos;
+       int ret = audio.render(samples[k], len1, pos);
+       while( !ret && !dialog->failed && cur_pos < end_pos ) {
+               dialog->update_progress(len1);
+               pos = cur_pos;
+               cur_pos = nxt_pos;
+               nxt_pos += len2;
+               if( nxt_pos > end_pos ) nxt_pos = end_pos;
+               len1 = nxt_pos - cur_pos;
+               ret = audio.render(samples[1-k], len1, cur_pos);
+               if( ret ) break;
                Track *track = edl->tracks->first;
                for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
-                       int id = track->mixer_id, atk = atracks.size();
-                       while( --atk >= 0 && id != atracks[atk]->track->mixer_id );
+                       int id = track->mixer_id, atk = dialog->atracks.size();
+                       while( --atk >= 0 && id != dialog->atracks[atk]->track->mixer_id );
                        if( atk < 0 ) continue;
                        int i = 0;
                        double *lp = samples[k][ch]->get_data();
@@ -895,28 +1413,28 @@ void MixersAlign::process_package(MixersAlignFarm *farm,
                        double *np = samples[1-k][ch]->get_data();
                        for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
                        while( i < len ) audio_r[i++] = 0;
-                       do_fft(len, 0, audio_r, 0, audio_r, audio_i);
-                       conj_product(len, audio_r, audio_i,
-                               audio_r, audio_i, master_r, master_i);
-                       do_fft(len, 1, audio_r, audio_i, audio_r, audio_i);
+                       fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
+                       conj_product(len, audio_r, audio_i, audio_r, audio_i,
+                                       dialog->master_r, dialog->master_i);
+                       fft.do_fft(len, 1, audio_r, audio_i);
                        double mx = 0;  int64_t mi = -1;
                        for( int i=0; i<len2; ++i ) {
                                double v = fabs(audio_r[i]);
                                if( mx < v ) { mx = v;  mi = i + pos; }
                        }
-                       mx /= master_ss;
-                       MixersAlignATrack *atrack= atracks[atk];
+                       mx /= dialog->master_ss;
+                       MixersAlignATrack *atrack= dialog->atracks[atk];
                        if( atrack->mx < mx ) {
                                atrack->mx = mx;
                                atrack->mi = mi;
                        }
                }
                k = 1-k;
-               if( progress->is_cancelled() ) failed = -1;
+               if( dialog->progress->is_cancelled() ) dialog->failed = -1;
        }
-       if( ret && !failed ) {
+       if( ret && !dialog->failed ) {
                eprintf("Audio render failed:\n%s", edl->path);
-               failed = 1;
+               dialog->failed = 1;
        }
        delete [] audio_r;
        delete [] audio_i;
@@ -928,6 +1446,138 @@ void MixersAlign::process_package(MixersAlignFarm *farm,
 }
 
 
+MixersAlignMatchRevFarm::MixersAlignMatchRevFarm(int n, int cpus,
+               MixersAlign *dialog, double *ar, double *ai, int len)
+ : LoadServer(n, cpus)
+{
+       this->dialog = dialog;
+       this->ar = ar;
+       this->ai = ai;
+       this->len = len;
+       mixer_lock = new Mutex("MixersAlignMatchRevFarm::mixer_lock");
+       pos = -1;
+}
+MixersAlignMatchRevFarm::~MixersAlignMatchRevFarm()
+{
+       delete mixer_lock;
+}
+
+MixersAlignMatchRevPackage::MixersAlignMatchRevPackage()
+{
+       mix = 0;
+}
+MixersAlignMatchRevPackage::~MixersAlignMatchRevPackage()
+{
+}
+
+void MixersAlignMatchRevFarm::init_packages()
+{
+       for( int i=0,m=0,n=dialog->mixers.size(); m<n; ++m ) {
+               if( !dialog->mixers[m]->br ) continue;
+               MixersAlignMatchRevPackage *pkg = (MixersAlignMatchRevPackage *)get_package(i++);
+               pkg->mix = dialog->mixers[m];
+       }
+}
+
+LoadClient *MixersAlignMatchRevFarm::new_client()
+{
+       return new MixersAlignMatchRevClient(this);
+}
+LoadPackage *MixersAlignMatchRevFarm::new_package()
+{
+       return new MixersAlignMatchRevPackage();
+}
+
+void MixersAlignMatchRevClient::process_package(LoadPackage *package)
+{
+       MixersAlignMatchRevFarm *farm = (MixersAlignMatchRevFarm *)server;
+       MixersAlign *dialog = farm->dialog;
+       if( dialog->progress->is_cancelled() ) dialog->failed = -1;
+       if( dialog->failed ) return;
+       pkg = (MixersAlignMatchRevPackage *)package;
+       MixersAlignMixer *mix = pkg->mix;
+       if( mix->aidx < 0 ) return;
+       int64_t ss = dialog->atracks[mix->aidx]->ss;
+
+       conj_product(farm->len, re, im, farm->ar, farm->ai, mix->br, mix->bi);
+       FFT fft;
+       fft.do_fft(farm->len, 1, re, im);
+       double mx = 0;  int64_t mi = -1;
+       for( int i=0,n=farm->len/2; i<n; ++i ) {
+               double r = fabs(re[i]) / ss;
+               if( mx < r ) {
+                       mx = r;
+                       mi = i + farm->pos;
+               }
+       }
+       farm->mixer_lock->lock("MixersAlignMatchRevFarm::process_package");
+       if( mix->mx < mx ) {
+               mix->mx = mx;
+               mix->mi = mi;
+printf("best %d: %f at %jd\n", get_package_number(), mx, mi);
+       }
+       farm->mixer_lock->unlock();
+}
+
+MixersAlignMatchRevClient::MixersAlignMatchRevClient(MixersAlignMatchRevFarm *farm)
+{
+       re = new double[farm->len];
+       im = new double[farm->len];
+}
+MixersAlignMatchRevClient::~MixersAlignMatchRevClient()
+{
+       delete [] re;
+       delete [] im;
+}
+
+void MixersAlign::start_progress(int64_t total_len)
+{
+       total_rendered = 0;
+       mwindow->gui->lock_window("MixersAlign::start_progress");
+       progress = mwindow->mainprogress->
+               start_progress(_("match mixer audio"), total_len);
+       mwindow->gui->unlock_window();
+}
+void MixersAlign::stop_progress(const char *msg)
+{
+       mwindow->gui->lock_window("MixersAlign::stop_progress");
+       progress->update(0);
+       mwindow->mainprogress->end_progress(progress);
+       progress = 0;
+       if( msg ) mwindow->gui->show_message(msg);
+       mwindow->gui->unlock_window();
+}
+
+void MixersAlign::reset_targets()
+{
+       for( int m=0,n=mixers.size(); m<n; ++m ) {
+               MixersAlignMixer *mix = mixers[m];
+               delete mix->br;  mix->br = 0;
+               delete mix->bi;  mix->bi = 0;
+               mix->mi = 0;
+               mix->aidx = -1;
+       }
+       for( int i=0,n=atracks.size(); i<n; ++i ) {
+               MixersAlignATrack *atrk = atracks[i];
+               atrk->nudge = 0; atrk->ss = 0;
+               atrk->mx = 0;    atrk->mi = -1;
+       }
+}
+
+void MixersAlign::scan_targets()
+{
+       int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
+       int midx = mmixer_of(idx);
+       int64_t total_len = mixer_tracks_total(midx);
+       start_progress(total_len);
+       int m = mixers.size();
+       if( midx >= 0 ) --m;
+       int cpus = bmin(mwindow->preferences->processors, m);
+       MixersAlignScanFarm scan(this, cpus, m);
+       scan.process_packages();
+       stop_progress(0);
+}
+
 void MixersAlign::update_progress(int64_t len)
 {
        total_lock->lock();
@@ -948,8 +1598,8 @@ void MixersAlign::load_master_audio(Track *track)
 {
        EDL *edl = mixer_master_clip(track);
        MixersAlignARender audio(mwindow, edl);
-       int sample_rate = edl->get_sample_rate();
        int channels = edl->get_audio_channels();
+       int64_t sample_rate = edl->get_sample_rate();
        int64_t audio_samples = edl->get_audio_samples();
        int64_t cur_pos = master_start * sample_rate;
        int64_t end_pos = master_end * sample_rate;
@@ -971,19 +1621,20 @@ void MixersAlign::load_master_audio(Track *track)
                delete [] master_r;  master_r = new double[master_len];
                delete [] master_i;  master_i = new double[master_len];
        }
-       {       Samples *samples[MAX_CHANNELS];
-               for( int i=0; i<MAX_CHANNELS; ++i )
-                       samples[i] = i<channels ? new Samples(audio_len) : 0;
-               int ret = audio.render(samples, audio_len, cur_pos);
-               if( ret ) failed = 1;
-               edl->remove_user();
+
+       Samples *samples[MAX_CHANNELS];
+       for( int i=0; i<MAX_CHANNELS; ++i )
+               samples[i] = i<channels ? new Samples(audio_len) : 0;
+       int ret = audio.render(samples, audio_len, cur_pos);
+       if( ret ) failed = 1;
+       edl->remove_user();
+       if( !failed ) {
                double *dp = samples[0]->get_data();
                int i = 0;  double ss = 0;
                for( ; i<audio_len; ++i ) { ss += *dp * *dp;  master_r[i] = *dp++; }
                master_ss = ss;
                for( ; i<fft_len; ++i ) master_r[i] = 0;
-               for( int i=channels; --i>=0; )
-                       delete samples[i];
+               for( int i=channels; --i>=0; ) delete samples[i];
        }
        do_fft(fft_len, 0, master_r, 0, master_r, master_i);
 }
@@ -999,13 +1650,9 @@ void MixersAlign::scan_mixer_audio()
        }
 
        Timer timer;
-       total_rendered = 0;
-       MixersAlignFarm farm(this, n);
-       int64_t total_len = mixer_tracks_total();
-       mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
-       progress = mwindow->mainprogress->
-               start_progress(_("Render mixer audio"), total_len);
-       mwindow->gui->unlock_window();
+       MixersAlignMatchFwdFarm farm(this, n);
+       int64_t total_len = mixer_tracks_total(-1);
+       start_progress(total_len);
 
        farm.process_packages();
        if( progress->is_cancelled() ) failed = -1;
@@ -1013,18 +1660,13 @@ void MixersAlign::scan_mixer_audio()
        char text[BCSTRLEN];
        double secs = timer.get_difference()/1000.;
        sprintf(text, _("Render mixer done: %0.3f secs"), secs);
-       mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
-       progress->update(0);
-       mwindow->mainprogress->end_progress(progress);
-       progress = 0;
-       mwindow->gui->show_message(text);
-       mwindow->gui->unlock_window();
+       stop_progress(text);
 }
 
-void MixersAlign::update_match()
+void MixersAlign::match_fwd()
 {
-       int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
-       if( midx < 0 ) {
+       int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
+       if( idx < 0 ) {
                eprintf(_("selection (master) not set"));
                return;
        }
@@ -1050,11 +1692,14 @@ void MixersAlign::update_match()
        }
 
        failed = 0;
-       if( !failed ) load_master_audio(mtracks[midx]->track);
-        if( !failed ) scan_mixer_audio();
-       if( !failed ) update();
+       if( !failed )
+               load_master_audio(mtracks[idx]->track);
+        if( !failed )
+               scan_mixer_audio();
+       if( !failed )
+               update_fwd();
        if( failed < 0 ) {
-               mwindow->gui->lock_window("MixersAlign::update_match");
+               mwindow->gui->lock_window("MixersAlign::update_match_fwd");
                mwindow->gui->show_message(_("mixer selection match canceled"));
                mwindow->gui->unlock_window();
        }
@@ -1062,8 +1707,44 @@ void MixersAlign::update_match()
                eprintf(_("Error in match render."));
 }
 
-void MixersAlign::update()
+void MixersAlign::match_rev()
+{
+       int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
+       if( midx < 0 ) {
+               eprintf(_("selection (master) not set"));
+               return;
+       }
+       Track *track = mtracks[midx]->track;
+       audio_start = mwindow->edl->local_session->get_selectionstart();
+       audio_end = mwindow->edl->local_session->get_selectionend();
+       if( audio_start >= audio_end ) {
+               eprintf(_("selection (audio start/end) invalid"));
+               return;
+       }
+
+       reset_targets();
+       failed = 0;
+
+       if( !failed )
+               scan_targets();
+        if( !failed )
+               scan_master(track);
+       if( !failed )
+               update_rev();
+
+       if( failed < 0 ) {
+               mwindow->gui->lock_window("MixersAlign::update_match_rev");
+               mwindow->gui->show_message(_("mixer selection match canceled"));
+               mwindow->gui->unlock_window();
+       }
+       else if( failed > 0 )
+               eprintf(_("Error in match render."));
+}
+
+void MixersAlign::update_fwd()
 {
+       double sample_rate = mwindow->edl->get_sample_rate();
+       int64_t mi = master_start * sample_rate;
 // mixer best matches
        for( int m=0; m<mixers.size(); ++m ) {
                MixersAlignMixer *mix = mixers[m];
@@ -1074,8 +1755,8 @@ void MixersAlign::update()
                        while( --k >= 0 && atracks[k]->track->mixer_id != id );
                        if( k < 0 ) continue;
                        MixersAlignATrack *atrk = atracks[k];
-                       atrk->nudge = atrk->mi < 0 ? 0 :
-                               master_start - atrk->track->from_units(atrk->mi);
+                       atrk->nudge = atrk->mi < 0 ? -1 :
+                               (mi - atrk->mi) / sample_rate;
                        if( mix->mx >= atrk->mx ) continue;
                        mix->aidx = k;
                        mix->mx = atrk->mx;
@@ -1083,7 +1764,29 @@ void MixersAlign::update()
                        mix->nudge = atrk->nudge;
                }
        }
+       update();
+}
+
+void MixersAlign::update_rev()
+{
+       int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
+       int midx = mmixer_of(idx);
+       double sample_rate = mwindow->edl->get_sample_rate();
+       for( int m=0,n=mixers.size(); m<n; ++m ) {
+               if( m == midx ) continue;
+               if( !ma_gui->mixer_list->is_selected(m) ) continue;
+               MixersAlignMixer *mix = mixers[m];
+               if( mix->aidx < 0 ) continue;
+               MixersAlignATrack *atrk = atracks[mix->aidx];
+               mix->nudge = atrk->mi < 0 ? -1 :
+                       (mix->mi - atrk->mi) / sample_rate;
+               atrk->mx = mix->mx;
+       }
+       update();
+}
 
+void MixersAlign::update()
+{
        ma_gui->lock_window("MixersAlign::update");
        ma_gui->mixer_list->load_list();
        ma_gui->mixer_list->set_all_selected(1);
index 86abbe6e8e76f3138d04dc67b05b62c12803addb..92b36baed2de1dad623b6e00dca250ae03b3a511 100644 (file)
@@ -21,7 +21,8 @@
 #ifndef __MIXERSALIGN_H__
 #define __MIXERSALIGN_H__
 
-#include "edl.inc"
+#include "edl.h"
+#include "edit.inc"
 #include "fourier.h"
 #include "guicast.h"
 #include "language.h"
@@ -38,10 +39,12 @@ class MixersAlignMixer
 {
 public:
        MixersAlignMixer(Mixer *mix);
+       ~MixersAlignMixer();
        Mixer *mixer;
        double nudge;
        double mx;
        int64_t mi;
+       double *br, *bi;
        int aidx;
 };
 
@@ -132,9 +135,12 @@ class MixersAlignATrack
 {
 public:
        MixersAlignATrack(Track *trk, int no);
+       ~MixersAlignATrack();
+
        Track *track;
        int no;
        double nudge;
+       double ss;
        double mx;
        int64_t mi;
 };
@@ -190,9 +196,11 @@ class MixersAlignThread : public Thread
 public:
        MixersAlignThread(MixersAlign *dialog);
        ~MixersAlignThread();
+       void start(int fwd);
        void run();
 
        MixersAlign *dialog;
+       int fwd;
 };
 
 class MixersAlignMatch : public BC_GenericButton
@@ -205,10 +213,20 @@ public:
        MixersAlignWindow *gui;
 };
 
-class MixersAlignApply : public BC_GenericButton
+class MixersAlignMatchAll : public BC_GenericButton
 {
 public:
-       MixersAlignApply(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
+       MixersAlignMatchAll(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
+       int handle_event();
+
+       MixersAlign *dialog;
+       MixersAlignWindow *gui;
+};
+
+class MixersAlignNudgeTracks : public BC_GenericButton
+{
+public:
+       MixersAlignNudgeTracks(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
        int handle_event();
        static int calculate_width(BC_WindowBase *gui);
 
@@ -216,16 +234,58 @@ public:
        MixersAlignWindow *gui;
 };
 
-class MixersAlignUndo : public BC_GenericButton
+class MixersAlignNudgeSelected : public BC_GenericButton
 {
 public:
-       MixersAlignUndo(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
+       MixersAlignNudgeSelected(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
+       int handle_event();
+       static int calculate_width(BC_WindowBase *gui);
+
+       MixersAlign *dialog;
+       MixersAlignWindow *gui;
+};
+
+class MixersAlignCheckPoint : public BC_GenericButton
+{
+public:
+       MixersAlignCheckPoint(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
        int handle_event();
 
        MixersAlign *dialog;
        MixersAlignWindow *gui;
 };
 
+class MixersAlignUndoEDLs : public ArrayList<EDL *>
+{
+public:
+       MixersAlignUndoEDLs() {}
+       ~MixersAlignUndoEDLs() {
+               for( int i=size(); --i>=0; ) get(i)->remove_user();
+       }
+};
+
+class MixersAlignUndoItem : public BC_MenuItem
+{
+public:
+       MixersAlignUndoItem(const char *text, int no);
+       ~MixersAlignUndoItem();
+       int handle_event();
+
+       int no;
+};
+
+class MixersAlignUndo : public BC_PopupMenu
+{
+public:
+       MixersAlignUndo(MixersAlignWindow *gui, MixersAlign *dialog, int x, int y);
+       ~MixersAlignUndo();
+       void create_objects();
+       void add_undo_item(int no);
+
+       MixersAlign *dialog;
+       MixersAlignWindow *gui;
+};
+
 
 class MixersAlignWindow : public BC_Window
 {
@@ -246,8 +306,11 @@ public:
        MixersAlignMTrackList *mtrack_list;
        MixersAlignATrackList *atrack_list;
        MixersAlignMatch *match;
+       MixersAlignMatchAll *match_all;
        MixersAlignReset *reset;
-       MixersAlignApply *apply;
+       MixersAlignNudgeTracks *nudge_tracks;
+       MixersAlignNudgeSelected *nudge_selected;
+       MixersAlignCheckPoint *check_point;
        MixersAlignUndo *undo;
 };
 
@@ -261,36 +324,151 @@ public:
        int render(Samples **samples, int64_t len, int64_t pos);
 };
 
-class MixersAlignPackage : public LoadPackage
+
+class MixersAlignScanPackage : public LoadPackage
 {
 public:
-       MixersAlignPackage();
-       ~MixersAlignPackage();
+       MixersAlignScanPackage(MixersAlignScanFarm *farm);
+       ~MixersAlignScanPackage();
 
        MixersAlignMixer *mixer;
 };
 
-class MixersAlignClient : public LoadClient
+class MixersAlignScanClient : public LoadClient
+{
+public:
+       MixersAlignScanClient(MixersAlignScanFarm *farm);
+       ~MixersAlignScanClient();
+        void process_package(LoadPackage *package);
+
+       MixersAlignScanFarm *farm;
+       MixersAlignScanPackage *pkg;
+       int64_t pos;
+       int len1;
+};
+
+class MixersAlignScanFarm : public LoadServer
 {
 public:
-       MixersAlignClient(MixersAlignFarm *farm);
-       ~MixersAlignClient();
+       MixersAlignScanFarm(MixersAlign *dialog, int cpus, int n);
+       ~MixersAlignScanFarm();
+       void init_packages();
+       LoadClient *new_client();
+       LoadPackage *new_package();
 
-        void process_package(LoadPackage *pkg);
+       MixersAlign *dialog;
+       Samples *samples;
+       int len;
 };
 
-class MixersAlignFarm : public LoadServer
+
+class MixersAlignMatchFwdPackage : public LoadPackage
 {
 public:
-       MixersAlignFarm(MixersAlign *dialog, int n);
-       ~MixersAlignFarm();
+       MixersAlignMatchFwdPackage();
+       ~MixersAlignMatchFwdPackage();
+
+       MixersAlignMixer *mixer;
+};
+
+class MixersAlignMatchFwdClient : public LoadClient
+{
+public:
+       MixersAlignMatchFwdClient(MixersAlignMatchFwdFarm *farm);
+       ~MixersAlignMatchFwdClient();
+
+        void process_package(LoadPackage *package);
+       MixersAlignMatchFwdPackage *pkg;
+};
+
+class MixersAlignMatchFwdFarm : public LoadServer
+{
+public:
+       MixersAlignMatchFwdFarm(MixersAlign *dialog, int n);
+       ~MixersAlignMatchFwdFarm();
+       void init_packages();
+       LoadClient *new_client();
+       LoadPackage *new_package();
+
+       MixersAlign *dialog;
+};
+
+
+class MixersAlignMatchRevPackage : public LoadPackage
+{
+public:
+       MixersAlignMatchRevPackage();
+       ~MixersAlignMatchRevPackage();
+
+       MixersAlignMixer *mix;
+};
+
+class MixersAlignMatchRevClient : public LoadClient
+{
+public:
+       MixersAlignMatchRevClient(MixersAlignMatchRevFarm *farm);
+       ~MixersAlignMatchRevClient();
+
+        void process_package(LoadPackage *package);
+       MixersAlignMatchRevPackage *pkg;
+       double *re, *im;
+};
+
+class MixersAlignMatchRevFarm : public LoadServer
+{
+public:
+       MixersAlignMatchRevFarm(int n, int cpus,
+               MixersAlign *dialog, double *ar, double *ai, int len);
+       ~MixersAlignMatchRevFarm();
        void init_packages();
        LoadClient *new_client();
        LoadPackage *new_package();
 
        MixersAlign *dialog;
+       Mutex *mixer_lock;
+       double *ar, *ai;
+       int len;
+       int64_t pos;
 };
 
+
+class MixersAlignTargetPackage : public LoadPackage
+{
+public:
+       MixersAlignTargetPackage(MixersAlignTarget *pfft);
+       ~MixersAlignTargetPackage();
+
+       double ss, sd2;
+       int64_t pos;
+       double *best;
+};
+
+class MixersAlignTargetClient : public LoadClient
+{
+public:
+       MixersAlignTargetClient();
+       ~MixersAlignTargetClient();
+
+       void process_package(LoadPackage *package);
+       MixersAlignTargetPackage *pkg;
+};
+
+class MixersAlignTarget : public LoadServer
+{
+public:
+       MixersAlignTarget(int n, int cpus,
+               MixersAlignScanClient *scan, Samples **samples, int len);
+       ~MixersAlignTarget();
+       void init_packages();
+       LoadClient *new_client();
+       LoadPackage *new_package();
+
+       MixersAlignScanClient *scan;
+       Samples **samples;
+       int len;
+};
+
+
 class MixersAlign : public BC_DialogThread, public FFT
 {
 public:
@@ -305,6 +483,7 @@ public:
        void handle_done_event(int result);
        void handle_close_event(int result);
 
+       int atrack_of(MixersAlignMixer *mix, int ch);
        int mixer_of(Track *track, int &midx);
        int mixer_of(Track *track) { int midx = -1; return mixer_of(track, midx); }
        int mmixer_of(int mi) {
@@ -316,14 +495,25 @@ public:
 
        EDL *mixer_audio_clip(Mixer *mixer);
        EDL *mixer_master_clip(Track *track);
-       int64_t mixer_tracks_total();
+       int64_t mixer_tracks_total(int midx);
        void load_master_audio(Track *track);
        void scan_mixer_audio();
+       void start_progress(int64_t total_len);
+       void stop_progress(const char *msg);
        void update_progress(int64_t len);
-       void update_match();
+       void match_fwd();
+       void match_rev();
+       void update_fwd();
+       void update_rev();
        void update();
-       void process_package(MixersAlignFarm *farm, MixersAlignPackage *package);
-       void apply();
+       void apply_undo(int no);
+       void nudge_tracks();
+       void nudge_selected();
+       void clear_mixer_nudge();
+       void check_point();
+       void reset_targets();
+       void scan_targets();
+       void scan_master(Track *track);
 
        MixersAlignWindow *ma_gui;
        int wx, wy;
@@ -332,14 +522,14 @@ public:
        MixersAlignATracks atracks;
        MWindow *mwindow;
 
-       EDL *undo_edl;
+       MixersAlignUndoEDLs undo_edls;
        Mutex *farming;
        MainProgressBar *progress;
        MixersAlignThread *thread;
        Mutex *total_lock;
        int64_t total_rendered;
        int failed;
-       int64_t master_len;
+       int64_t master_len, sample_len;
        double *master_r, *master_i;
        double master_start, master_end, master_ss;
        double audio_start, audio_end;
index 7e17048a30a0a6091ae4115bc2b32adc87cabff1..a8e0edfc5590b98934177acdce52d3925663f747 100644 (file)
@@ -10,11 +10,30 @@ class MixersAlignMTrackList;
 class MixersAlignATrack;
 class MixersAlignATracks;
 class MixersAlignATrackList;
+class MixersAlignReset;
+class MixersAlignThread;
+class MixersAlignMatch;
+class MixersAlignMatchAll;
+class MixersAlignNudgeTracks;
+class MixersAlignNudgeSelected;
+class MixersAlignCheckPoint;
+class MixersAlignUndoEDLs;
+class MixersAlignUndoItem;
+class MixersAlignUndo;
 class MixersAlignWindow;
 class MixersAlignARender;
-class MixersAlignPackage;
-class MixersAlignClient;
-class MixersAlignFarm;
+class MixersAlignScanPackage;
+class MixersAlignScanClient;
+class MixersAlignScanFarm;
+class MixersAlignMatchFwdPackage;
+class MixersAlignMatchFwdClient;
+class MixersAlignMatchFwdFarm;
+class MixersAlignMatchRevPackage;
+class MixersAlignMatchRevClient;
+class MixersAlignMatchRevFarm;
+class MixersAlignTargetPackage;
+class MixersAlignTargetClient;
+class MixersAlignTarget;
 class MixersAlign;
 
 #endif
index 9a49f178ceb33b3d0765ad37e1d6235953244629..546444fe2bbd6b053ca1450298e2e4fc3ecdd9ad 100644 (file)
@@ -1334,12 +1334,12 @@ void MWindow::close_mixers(int destroy)
        }
 }
 
-ZWindow *MWindow::create_mixer(Indexable *indexable)
+ZWindow *MWindow::create_mixer(Indexable *indexable, double position)
 {
        ArrayList<Indexable*> new_assets;
        new_assets.append(indexable);
        Track *track = edl->tracks->last;
-       load_assets(&new_assets, 0, LOADMODE_NEW_TRACKS, 0, 0, 0, 0, 0, 0);
+       load_assets(&new_assets, position, LOADMODE_NEW_TRACKS, 0, 0, 0, 0, 0, 0);
        track = !track ? edl->tracks->first : track->next;
        Mixer *mixer = 0;
        ZWindow *zwindow = get_mixer(mixer);
@@ -1364,7 +1364,7 @@ ZWindow *MWindow::create_mixer(Indexable *indexable)
        return zwindow;
 }
 
-void MWindow::create_mixers()
+void MWindow::create_mixers(double position)
 {
        if( !session->drag_assets->size() &&
            !session->drag_clips->size() ) return;
@@ -1376,13 +1376,13 @@ void MWindow::create_mixers()
        for( int i=0; i<session->drag_assets->size(); ++i ) {
                Indexable *indexable = session->drag_assets->get(i);
                if( !indexable->have_video() ) continue;
-               ZWindow *zwindow = create_mixer(indexable);
+               ZWindow *zwindow = create_mixer(indexable, position);
                new_mixers.append(zwindow);
        }
        for( int i=0; i<session->drag_clips->size(); ++i ) {
                Indexable *indexable = (Indexable*)session->drag_clips->get(i);
                if( !indexable->have_video() ) continue;
-               ZWindow *zwindow = create_mixer(indexable);
+               ZWindow *zwindow = create_mixer(indexable, position);
                new_mixers.append(zwindow);
        }
 
index 0331d5c6282a4d6cd51439ae7387a404db6df1d8..be468ac9249c107e99fa2f3bab8f056e445f75e1 100644 (file)
@@ -183,10 +183,6 @@ public:
        void update_vwindow();
 // Fit selected time to horizontal display range
        void fit_selection();
-       EDL *selected_edits_to_clip(int packed,
-               double *start_position, Track **start_track,
-               int edit_labels, int edit_autos, int edit_plugins);
-       void selected_edits_to_clipboard(int packed);
 // Fit selected autos to the vertical display range
        void fit_autos(int all);
        void change_currentautorange(int autogrouptype, int increment, int changemax);
@@ -215,8 +211,8 @@ public:
 
        void handle_mixers(EDL *edl, int command, int wait_tracking,
                int use_inout, int toggle_audio, int loop_play, float speed);
-       ZWindow *create_mixer(Indexable *indexable);
-       void create_mixers();
+       ZWindow *create_mixer(Indexable *indexable, double position);
+       void create_mixers(double position = 0);
        void refresh_mixers(int dir=1);
        void stop_mixers();
        void close_mixers(int destroy=1);
@@ -409,8 +405,7 @@ public:
 // Move edit to new position
        void move_edits(ArrayList<Edit*> *edits, Track *track, double position,
                int mode); // mode: 0 - mute and overwrite,  1 - cut and paste
-       void paste_edits(EDL *clip, Track *first_track, double position, int overwrite,
-               int edit_edits, int edit_labels, int edit_autos, int edit_plugins);
+       void selected_edits_to_clipboard(int packed);
        void paste_clipboard(Track *first_track, double position, int overwrite,
                int edit_edits, int edit_labels, int edit_autos, int edit_plugins);
        void move_group(EDL *group, Track *first_track, double position, int overwrite);
index b59c2bdf85c56303fc618b52f2f4ddd270ba227d..bf5dc9177388679677bf9b01de192ff7497221d7 100644 (file)
@@ -914,190 +914,6 @@ void MWindow::match_output_size(Track *track)
 }
 
 
-EDL *MWindow::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=edl->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(edl);
-       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=edl->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->copy_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 = edl->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->position<clip_position )
-                                               clip_label = clip_label->next;
-                                       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; i<AUTOMATION_TOTAL; ++i ) {
-                                       Autos *autos = automation->autos[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; i<track->plugin_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;
-}
-
-void MWindow::selected_edits_to_clipboard(int packed)
-{
-       EDL *new_edl = selected_edits_to_clip(packed, 0, 0,
-               edl->session->labels_follow_edits,
-               edl->session->autos_follow_edits,
-               edl->session->plugins_follow_edits);
-       if( !new_edl ) return;
-       FileXML file;
-       new_edl->copy(COPY_EDL, &file, "", 1);
-       const char *file_string = file.string();
-       long file_length = strlen(file_string);
-       gui->to_clipboard(file_string, file_length, BC_PRIMARY_SELECTION);
-       gui->to_clipboard(file_string, file_length, SECONDARY_SELECTION);
-       new_edl->remove_user();
-}
-
 void MWindow::delete_edit(Edit *edit, const char *msg, int collapse)
 {
        ArrayList<Edit*> edits;
@@ -1164,131 +980,20 @@ void MWindow::move_edits(ArrayList<Edit*> *edits,
        gui->update(1, NORMAL_DRAW, 1, 0, 0, 0, 0);
 }
 
-void MWindow::paste_edits(EDL *clip, Track *first_track, double position, int overwrite,
-               int edit_edits, int edit_labels, int edit_autos, int edit_plugins)
+void MWindow::selected_edits_to_clipboard(int packed)
 {
-       if( !first_track )
-               first_track = edl->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->copy_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=edl->labels->first; label; label=label->next ) {
-                                               if( label->position >= dst_pos )
-                                                       label->position += dst_len;
-                                       }
-                               }
-                               if( edit_autos ) {
-                                       for( int i=0; i<AUTOMATION_TOTAL; ++i ) {
-                                               Autos *autos = track->automation->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; i<track->plugin_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; i<AUTOMATION_TOTAL; ++i ) {
-                               Autos *src_autos = src->automation->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; i<src->plugin_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(edl, 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);
-                                       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 = edl->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=edl->equivalent(edl_label->position, label_pos)) &&
-                               edl_label->position < position ) edl_label = edl_label->next;
-                       if( exists ) continue;
-                       edl->labels->insert_before(edl_label,
-                               new Label(edl, edl->labels, label_pos, label->textstr));
-               }
-       }
-       edl->optimize();
+       EDL *new_edl = edl->selected_edits_to_clip(packed, 0, 0,
+               edl->session->labels_follow_edits,
+               edl->session->autos_follow_edits,
+               edl->session->plugins_follow_edits);
+       if( !new_edl ) return;
+       FileXML file;
+       new_edl->copy(COPY_EDL, &file, "", 1);
+       const char *file_string = file.string();
+       long file_length = strlen(file_string);
+       gui->to_clipboard(file_string, file_length, BC_PRIMARY_SELECTION);
+       gui->to_clipboard(file_string, file_length, SECONDARY_SELECTION);
+       new_edl->remove_user();
 }
 
 void MWindow::paste_clipboard(Track *first_track, double position, int overwrite,
@@ -1305,7 +1010,7 @@ void MWindow::paste_clipboard(Track *first_track, double position, int overwrite
        clip->create_objects();
        if( !clip->load_xml(&file, LOAD_ALL) ) {
                undo_before();
-               paste_edits(clip, first_track, position, overwrite,
+               edl->paste_edits(clip, first_track, position, overwrite,
                        edit_edits, edit_labels, edit_autos, edit_plugins);
                save_backup();
                undo_after(_("paste clip"), LOAD_ALL);
@@ -1330,7 +1035,7 @@ void MWindow::move_group(EDL *group, Track *first_track, double position, int ov
        if( edl->session->labels_follow_edits )
                edl->delete_edit_labels(&edits, 0);
        edl->delete_edits(&edits, 0);
-       paste_edits(group, first_track, position, overwrite, 1,
+       edl->paste_edits(group, first_track, position, overwrite, 1,
                edl->session->labels_follow_edits,
                edl->session->autos_follow_edits,
                edl->session->plugins_follow_edits);
@@ -1843,8 +1548,8 @@ int MWindow::paste_edls(ArrayList<EDL*> *new_edls, int load_mode,
 // Get starting point of insertion.  Need this to paste labels.
                switch( load_mode ) {
                case LOADMODE_REPLACE:
-               case LOADMODE_NEW_TRACKS:
                        current_position = 0;
+               case LOADMODE_NEW_TRACKS:
                        break;
 
                case LOADMODE_CONCATENATE:
index a0d3b6c2b494aa8976853ffabacd29206c5989aa..e1f1215e9f6beba10f7bd85ac7f7aa6062296269 100644 (file)
@@ -442,6 +442,13 @@ int PluginSet::optimize()
        int result = 1;
        Plugin *current_edit;
 
+// trim plugins before position 0
+       while( first && first->startproject+first->length < 0 )
+               delete first;
+       if( first && first->startproject < 0 ) {
+               first->length += first->startproject;
+               first->startproject = 0;
+       }
 
 // Delete keyframes out of range
        for(current_edit = (Plugin*)first;
index 0361c40fa127093bb9f85da67f44e1017ff56688..ffd9aef688638b0ed41acee22492dfaf66bbbf31 100644 (file)
@@ -122,8 +122,10 @@ int Save::handle_event()
 
 int Save::save_before_quit()
 {
+       mwindow->gui->lock_window("Save::save_before_quit");
        saveas->quit_now = 1;
        handle_event();
+       mwindow->gui->unlock_window();
        return 0;
 }
 
index cf8f1a2e4ffe869289b1ce95748b94e491c68e15..67366aa160789da444e7b130ead35d766c480e96 100644 (file)
@@ -330,7 +330,7 @@ void SetFormatWindow::create_objects()
        add_subwindow(new BC_Title(mwindow->theme->setformat_x1, y,
                _("Channel positions:")));
        y += mwindow->theme->setformat_margin;
-       add_subwindow(new SetChannelsReset(thread,
+       add_subwindow(channels_reset = new SetChannelsReset(thread,
                mwindow->theme->setformat_x1, y,
                _("Reset")));
        add_subwindow(canvas = new SetChannelsCanvas(mwindow,
@@ -508,7 +508,7 @@ SetFormatPresets::~SetFormatPresets()
 int SetFormatPresets::handle_event()
 {
        format_gui->thread->update();
-       return 1;
+       return format_gui->channels_reset->handle_event();
 }
 
 EDL* SetFormatPresets::get_edl()
index b85fce1752aa3e7f7a2346913daebfdecaca9f86..f7b275cc732dade09f70a3042f8d43bf39ec2870 100644 (file)
@@ -222,6 +222,7 @@ public:
 
        MWindow *mwindow;
        SetFormatThread *thread;
+       SetChannelsReset *channels_reset;
        SetChannelsCanvas *canvas;
 // Screen size width, height
        ScaleSizeText* dimension[2];
index 27eede528e536bc2a324936ac4fb7d943a3dac89..748b8b2b8878ec25ba5a769709e7d3effd2d408f 100644 (file)
@@ -5025,11 +5025,8 @@ int TrackCanvas::do_edits(int cursor_x, int cursor_y, int button_press, int drag
                                                        mwindow->session->drag_group->remove_user();
                                                double start_position = 0;
                                                mwindow->session->drag_group =
-                                                       mwindow->selected_edits_to_clip(0, &start_position,
-                                                               &mwindow->session->drag_group_first_track,
-                                                               mwindow->edl->session->labels_follow_edits,
-                                                               mwindow->edl->session->autos_follow_edits,
-                                                               mwindow->edl->session->plugins_follow_edits);
+                                                       mwindow->edl->selected_edits_to_clip(0, &start_position,
+                                                               &mwindow->session->drag_group_first_track);
                                                if( mwindow->session->drag_group ) {
                                                        mwindow->session->current_operation = DRAG_GROUP;
                                                        mwindow->session->drag_group_position = start_position;
index dd834d566221cfb7e8f25444b1285d31624e6570..115a2f1d52eef089a2d405b5a8b693850b9ad85d 100644 (file)
@@ -120,7 +120,7 @@ int TransitionUnitsItem::handle_event()
 }
 
 TransitionUnitsPopup::TransitionUnitsPopup(TransitionLengthDialog *gui, int x, int y)
- : BC_PopupMenu(x, y, 100, "", 1)
+ : BC_PopupMenu(x, y, 120, "", 1)
 {
        this->gui = gui;
        units = TIME_SECONDS;