From: Good Guy Date: Sun, 31 Mar 2019 01:24:03 +0000 (-0600) Subject: ins mixers, match all and move for mixer align, cr in xml string kludge, save_as... X-Git-Tag: 2019-08~76 X-Git-Url: https://cinelerra-gg.org/git/?a=commitdiff_plain;h=a0ed17a5d6ecf2b010d977bc1f9b7226f24c20f6;p=goodguy%2Fcinelerra.git ins mixers, match all and move for mixer align, cr in xml string kludge, save_as booby, transition popup width tweak, set_format reset audio on preset --- diff --git a/cinelerra-5.1/cinelerra/assetpopup.C b/cinelerra-5.1/cinelerra/assetpopup.C index 102c803c..6b717309 100644 --- a/cinelerra-5.1/cinelerra/assetpopup.C +++ b/cinelerra-5.1/cinelerra/assetpopup.C @@ -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")) { diff --git a/cinelerra-5.1/cinelerra/assetpopup.h b/cinelerra-5.1/cinelerra/assetpopup.h index c30c155c..eea5a8ea 100644 --- a/cinelerra-5.1/cinelerra/assetpopup.h +++ b/cinelerra-5.1/cinelerra/assetpopup.h @@ -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(); diff --git a/cinelerra-5.1/cinelerra/assetpopup.inc b/cinelerra-5.1/cinelerra/assetpopup.inc index aeeb5d3f..4cdb174c 100644 --- a/cinelerra-5.1/cinelerra/assetpopup.inc +++ b/cinelerra-5.1/cinelerra/assetpopup.inc @@ -38,7 +38,8 @@ class AssetPopupSort; class AssetPopupBuildIndex; class AssetPopupView; class AssetPopupViewWindow; -class AssetPopupMixer; +class AssetPopupOpenMixer; +class AssetPopupInsertMixer; class AssetPopupPaste; class AssetMatchSize; class AssetMatchRate; diff --git a/cinelerra-5.1/cinelerra/edits.C b/cinelerra-5.1/cinelerra/edits.C index 9e3c2a5e..ae7ab0c7 100644 --- a/cinelerra-5.1/cinelerra/edits.C +++ b/cinelerra-5.1/cinelerra/edits.C @@ -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) { diff --git a/cinelerra-5.1/cinelerra/edl.C b/cinelerra-5.1/cinelerra/edl.C index 8312c71e..cc2e84fb 100644 --- a/cinelerra-5.1/cinelerra/edl.C +++ b/cinelerra-5.1/cinelerra/edl.C @@ -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->positionnext; + if( clip_label && clip_label->position == clip_position ) continue; + Label *new_label = new Label(new_edl, + new_edl->labels, clip_position, label->textstr); + new_edl->labels->insert_before(clip_label, new_label); + } + } + if( edit_autos ) { + Automation *automation = track->automation; + Automation *new_automation = new_track->automation; + for( int i=0; iautos[i]; + if( !autos ) continue; + Autos *new_autos = new_automation->autos[i]; + new_autos->default_auto->copy_from(autos->default_auto); + Auto *aut0 = autos->first; + for( ; aut0; aut0=aut0->next ) { + if( aut0->position < edit_start_pos ) continue; + if( aut0->position >= edit_end_pos ) break; + Auto *new_auto = new_autos->new_auto(); + new_auto->copy_from(aut0); + int64_t clip_position = aut0->position - edit_start_pos + clip_start_pos; + new_auto->position = clip_position; + new_autos->append(new_auto); + } + } + } + if( edit_plugins ) { + while( new_track->plugin_set.size() < track->plugin_set.size() ) + new_track->plugin_set.append(0); + for( int i=0; iplugin_set.total; ++i ) { + PluginSet *plugin_set = track->plugin_set[i]; + if( !plugin_set ) continue; + PluginSet *new_plugin_set = new_track->plugin_set[i]; + if( !new_plugin_set ) { + new_plugin_set = new PluginSet(new_edl, new_track); + new_track->plugin_set[i] = new_plugin_set; + } + Plugin *plugin = (Plugin*)plugin_set->first; + int64_t startplugin = new_plugin_set->length(); + for( ; plugin ; plugin=(Plugin*)plugin->next ) { + if( plugin->silence() ) continue; + int64_t plugin_start_pos = plugin->startproject; + int64_t plugin_end_pos = plugin_start_pos + plugin->length; + if( plugin_end_pos < start_pos ) continue; + if( plugin_start_pos > end_pos ) break; + if( plugin_start_pos < edit_start_pos ) + plugin_start_pos = edit_start_pos; + if( plugin_end_pos > edit_end_pos ) + plugin_end_pos = edit_end_pos; + if( plugin_start_pos >= plugin_end_pos ) continue; + int64_t plugin_pos = plugin_start_pos - start_pos; + if( !packed && plugin_pos > startplugin ) { + Plugin *silence = new Plugin(new_edl, new_track, ""); + silence->startproject = startplugin; + silence->length = plugin_pos - startplugin; + new_plugin_set->append(silence); + startplugin = plugin_pos; + } + Plugin *new_plugin = new Plugin(new_edl, new_track, plugin->title); + new_plugin->copy_base(plugin); + new_plugin->startproject = startplugin; + new_plugin->length = plugin_end_pos - plugin_start_pos; + startplugin += new_plugin->length; + new_plugin_set->append(new_plugin); + KeyFrames *keyframes = plugin->keyframes; + KeyFrames *new_keyframes = new_plugin->keyframes; + new_keyframes->default_auto->copy_from(keyframes->default_auto); + new_keyframes->default_auto->position = new_plugin->startproject; + KeyFrame *keyframe = (KeyFrame*)keyframes->first; + for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { + if( keyframe->position < edit_start_pos ) continue; + if( keyframe->position >= edit_end_pos ) break; + KeyFrame *clip_keyframe = new KeyFrame(new_edl, new_keyframes); + clip_keyframe->copy_from(keyframe); + int64_t key_position = keyframe->position - start_pos; + if( packed ) + key_position += new_plugin->startproject - plugin_pos; + clip_keyframe->position = key_position; + new_keyframes->append(clip_keyframe); + } + } + } + } + } + if( last_track == track ) break; + } + return new_edl; +} + +EDL *EDL::selected_edits_to_clip(int packed, double *start_position, Track **start_track) +{ + return selected_edits_to_clip(packed, start_position, start_track, + session->labels_follow_edits, + session->autos_follow_edits, + session->plugins_follow_edits); +} + +void EDL::paste_edits(EDL *clip, Track *first_track, double position, int overwrite, + int edit_edits, int edit_labels, int edit_autos, int edit_plugins) +{ + if( !first_track ) + first_track = tracks->first; + Track *src = clip->tracks->first; + for( Track *track=first_track; track && src; track=track->next ) { + if( !track->record ) continue; + int64_t pos = track->to_units(position, 0); + if( edit_edits ) { + for( Edit *edit=src->edits->first; edit; edit=edit->next ) { + if( edit->silence() ) continue; + int64_t start = pos + edit->startproject; + int64_t len = edit->length, end = start + len; + if( overwrite ) + track->edits->clear(start, end); + Edit *dst = track->edits->insert_new_edit(start); + dst->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; iautomation->autos[i]; + if( !autos ) continue; + for( Auto *aut0=autos->first; aut0; aut0=aut0->next ) { + if( aut0->position >= start ) + aut0->position += edit->length; + } + } + } + if( edit_plugins ) { + for( int i=0; iplugin_set.size(); ++i ) { + PluginSet *plugin_set = track->plugin_set[i]; + Plugin *plugin = (Plugin *)plugin_set->first; + for( ; plugin; plugin=(Plugin *)plugin->next ) { + if( plugin->startproject >= start ) + plugin->startproject += edit->length; + else if( plugin->startproject+plugin->length > end ) + plugin->length += edit->length; + Auto *default_keyframe = plugin->keyframes->default_auto; + if( default_keyframe->position >= start ) + default_keyframe->position += edit->length; + KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first; + for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { + if( keyframe->position >= start ) + keyframe->position += edit->length; + } + } + plugin_set->optimize(); + } + } + } + } + if( edit_autos ) { + for( int i=0; iautomation->autos[i]; + if( !src_autos ) continue; + Autos *autos = track->automation->autos[i]; + for( Auto *aut0=src_autos->first; aut0; aut0=aut0->next ) { + int64_t auto_pos = pos + aut0->position; + autos->insert_auto(auto_pos, aut0); + } + } + } + if( edit_plugins ) { + for( int i=0; iplugin_set.size(); ++i ) { + PluginSet *plugin_set = src->plugin_set[i]; + if( !plugin_set ) continue; + while( i >= track->plugin_set.size() ) + track->plugin_set.append(0); + PluginSet *dst_plugin_set = track->plugin_set[i]; + if( !dst_plugin_set ) { + dst_plugin_set = new PluginSet(this, track); + track->plugin_set[i] = dst_plugin_set; + } + Plugin *plugin = (Plugin *)plugin_set->first; + if( plugin ) track->expand_view = 1; + for( ; plugin; plugin=(Plugin *)plugin->next ) { + int64_t start = pos + plugin->startproject; + int64_t end = start + plugin->length; + if( overwrite || edit_edits ) + dst_plugin_set->clear(start, end, 1); + Plugin *new_plugin = dst_plugin_set->insert_plugin(plugin->title, + start, end-start, plugin->plugin_type, &plugin->shared_location, + (KeyFrame*)plugin->keyframes->default_auto, 0); + 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); +} + diff --git a/cinelerra-5.1/cinelerra/edl.h b/cinelerra-5.1/cinelerra/edl.h index f355b57a..0798b408 100644 --- a/cinelerra-5.1/cinelerra/edl.h +++ b/cinelerra-5.1/cinelerra/edl.h @@ -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 *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); diff --git a/cinelerra-5.1/cinelerra/filexml.C b/cinelerra-5.1/cinelerra/filexml.C index 58a83e9e..e045a532 100644 --- a/cinelerra-5.1/cinelerra/filexml.C +++ b/cinelerra-5.1/cinelerra/filexml.C @@ -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('='); diff --git a/cinelerra-5.1/cinelerra/mixersalign.C b/cinelerra-5.1/cinelerra/mixersalign.C index 188782c1..1e91b6ca 100644 --- a/cinelerra-5.1/cinelerra/mixersalign.C +++ b/cinelerra-5.1/cinelerra/mixersalign.C @@ -43,13 +43,31 @@ #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; imixer = 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; imixer_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 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; imixer_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; mnudge = 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(); imixer_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; ima_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; imwindow->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= 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; iis_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; isd2 < 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; ibr ) ++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; jget_data(); + for( int j=0; jis_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 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; chnext ) { - 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; jmaster_r, dialog->master_i); + fft.do_fft(len, 1, audio_r, audio_i); double mx = 0; int64_t mi = -1; for( int i=0; imaster_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(); mmixers[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; ipos; + } + } + 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(); mbr; mix->br = 0; + delete mix->bi; mix->bi = 0; + mix->mi = 0; + mix->aidx = -1; + } + for( int i=0,n=atracks.size(); inudge = 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; iremove_user(); + + Samples *samples[MAX_CHANNELS]; + for( int i=0; iremove_user(); + if( !failed ) { double *dp = samples[0]->get_data(); int i = 0; double ss = 0; for( ; 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= 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(); mmixer_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); diff --git a/cinelerra-5.1/cinelerra/mixersalign.h b/cinelerra-5.1/cinelerra/mixersalign.h index 86abbe6e..92b36bae 100644 --- a/cinelerra-5.1/cinelerra/mixersalign.h +++ b/cinelerra-5.1/cinelerra/mixersalign.h @@ -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 +{ +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; diff --git a/cinelerra-5.1/cinelerra/mixersalign.inc b/cinelerra-5.1/cinelerra/mixersalign.inc index 7e17048a..a8e0edfc 100644 --- a/cinelerra-5.1/cinelerra/mixersalign.inc +++ b/cinelerra-5.1/cinelerra/mixersalign.inc @@ -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 diff --git a/cinelerra-5.1/cinelerra/mwindow.C b/cinelerra-5.1/cinelerra/mwindow.C index 9a49f178..546444fe 100644 --- a/cinelerra-5.1/cinelerra/mwindow.C +++ b/cinelerra-5.1/cinelerra/mwindow.C @@ -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 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; idrag_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; idrag_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); } diff --git a/cinelerra-5.1/cinelerra/mwindow.h b/cinelerra-5.1/cinelerra/mwindow.h index 0331d5c6..be468ac9 100644 --- a/cinelerra-5.1/cinelerra/mwindow.h +++ b/cinelerra-5.1/cinelerra/mwindow.h @@ -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 *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); diff --git a/cinelerra-5.1/cinelerra/mwindowedit.C b/cinelerra-5.1/cinelerra/mwindowedit.C index b59c2bdf..bf5dc917 100644 --- a/cinelerra-5.1/cinelerra/mwindowedit.C +++ b/cinelerra-5.1/cinelerra/mwindowedit.C @@ -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->positionnext; - if( clip_label && clip_label->position == clip_position ) continue; - Label *new_label = new Label(new_edl, - new_edl->labels, clip_position, label->textstr); - new_edl->labels->insert_before(clip_label, new_label); - } - } - if( edit_autos ) { - Automation *automation = track->automation; - Automation *new_automation = new_track->automation; - for( int i=0; iautos[i]; - if( !autos ) continue; - Autos *new_autos = new_automation->autos[i]; - new_autos->default_auto->copy_from(autos->default_auto); - Auto *aut0 = autos->first; - for( ; aut0; aut0=aut0->next ) { - if( aut0->position < edit_start_pos ) continue; - if( aut0->position >= edit_end_pos ) break; - Auto *new_auto = new_autos->new_auto(); - new_auto->copy_from(aut0); - int64_t clip_position = aut0->position - edit_start_pos + clip_start_pos; - new_auto->position = clip_position; - new_autos->append(new_auto); - } - } - } - if( edit_plugins ) { - while( new_track->plugin_set.size() < track->plugin_set.size() ) - new_track->plugin_set.append(0); - for( int i=0; iplugin_set.total; ++i ) { - PluginSet *plugin_set = track->plugin_set[i]; - if( !plugin_set ) continue; - PluginSet *new_plugin_set = new_track->plugin_set[i]; - if( !new_plugin_set ) { - new_plugin_set = new PluginSet(new_edl, new_track); - new_track->plugin_set[i] = new_plugin_set; - } - Plugin *plugin = (Plugin*)plugin_set->first; - int64_t startplugin = new_plugin_set->length(); - for( ; plugin ; plugin=(Plugin*)plugin->next ) { - if( plugin->silence() ) continue; - int64_t plugin_start_pos = plugin->startproject; - int64_t plugin_end_pos = plugin_start_pos + plugin->length; - if( plugin_end_pos < start_pos ) continue; - if( plugin_start_pos > end_pos ) break; - if( plugin_start_pos < edit_start_pos ) - plugin_start_pos = edit_start_pos; - if( plugin_end_pos > edit_end_pos ) - plugin_end_pos = edit_end_pos; - if( plugin_start_pos >= plugin_end_pos ) continue; - int64_t plugin_pos = plugin_start_pos - start_pos; - if( !packed && plugin_pos > startplugin ) { - Plugin *silence = new Plugin(new_edl, new_track, ""); - silence->startproject = startplugin; - silence->length = plugin_pos - startplugin; - new_plugin_set->append(silence); - startplugin = plugin_pos; - } - Plugin *new_plugin = new Plugin(new_edl, new_track, plugin->title); - new_plugin->copy_base(plugin); - new_plugin->startproject = startplugin; - new_plugin->length = plugin_end_pos - plugin_start_pos; - startplugin += new_plugin->length; - new_plugin_set->append(new_plugin); - KeyFrames *keyframes = plugin->keyframes; - KeyFrames *new_keyframes = new_plugin->keyframes; - new_keyframes->default_auto->copy_from(keyframes->default_auto); - new_keyframes->default_auto->position = new_plugin->startproject; - KeyFrame *keyframe = (KeyFrame*)keyframes->first; - for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { - if( keyframe->position < edit_start_pos ) continue; - if( keyframe->position >= edit_end_pos ) break; - KeyFrame *clip_keyframe = new KeyFrame(new_edl, new_keyframes); - clip_keyframe->copy_from(keyframe); - int64_t key_position = keyframe->position - start_pos; - if( packed ) - key_position += new_plugin->startproject - plugin_pos; - clip_keyframe->position = key_position; - new_keyframes->append(clip_keyframe); - } - } - } - } - } - if( last_track == track ) break; - } - return new_edl; -} - -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 edits; @@ -1164,131 +980,20 @@ void MWindow::move_edits(ArrayList *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; iautomation->autos[i]; - if( !autos ) continue; - for( Auto *aut0=autos->first; aut0; aut0=aut0->next ) { - if( aut0->position >= start ) - aut0->position += edit->length; - } - } - } - if( edit_plugins ) { - for( int i=0; iplugin_set.size(); ++i ) { - PluginSet *plugin_set = track->plugin_set[i]; - Plugin *plugin = (Plugin *)plugin_set->first; - for( ; plugin; plugin=(Plugin *)plugin->next ) { - if( plugin->startproject >= start ) - plugin->startproject += edit->length; - else if( plugin->startproject+plugin->length > end ) - plugin->length += edit->length; - Auto *default_keyframe = plugin->keyframes->default_auto; - if( default_keyframe->position >= start ) - default_keyframe->position += edit->length; - KeyFrame *keyframe = (KeyFrame*)plugin->keyframes->first; - for( ; keyframe; keyframe=(KeyFrame*)keyframe->next ) { - if( keyframe->position >= start ) - keyframe->position += edit->length; - } - } - plugin_set->optimize(); - } - } - } - } - if( edit_autos ) { - for( int i=0; iautomation->autos[i]; - if( !src_autos ) continue; - Autos *autos = track->automation->autos[i]; - for( Auto *aut0=src_autos->first; aut0; aut0=aut0->next ) { - int64_t auto_pos = pos + aut0->position; - autos->insert_auto(auto_pos, aut0); - } - } - } - if( edit_plugins ) { - for( int i=0; iplugin_set.size(); ++i ) { - PluginSet *plugin_set = src->plugin_set[i]; - if( !plugin_set ) continue; - while( i >= track->plugin_set.size() ) - track->plugin_set.append(0); - PluginSet *dst_plugin_set = track->plugin_set[i]; - if( !dst_plugin_set ) { - dst_plugin_set = new PluginSet(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 *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: diff --git a/cinelerra-5.1/cinelerra/pluginset.C b/cinelerra-5.1/cinelerra/pluginset.C index a0d3b6c2..e1f1215e 100644 --- a/cinelerra-5.1/cinelerra/pluginset.C +++ b/cinelerra-5.1/cinelerra/pluginset.C @@ -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; diff --git a/cinelerra-5.1/cinelerra/savefile.C b/cinelerra-5.1/cinelerra/savefile.C index 0361c40f..ffd9aef6 100644 --- a/cinelerra-5.1/cinelerra/savefile.C +++ b/cinelerra-5.1/cinelerra/savefile.C @@ -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; } diff --git a/cinelerra-5.1/cinelerra/setformat.C b/cinelerra-5.1/cinelerra/setformat.C index cf8f1a2e..67366aa1 100644 --- a/cinelerra-5.1/cinelerra/setformat.C +++ b/cinelerra-5.1/cinelerra/setformat.C @@ -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() diff --git a/cinelerra-5.1/cinelerra/setformat.h b/cinelerra-5.1/cinelerra/setformat.h index b85fce17..f7b275cc 100644 --- a/cinelerra-5.1/cinelerra/setformat.h +++ b/cinelerra-5.1/cinelerra/setformat.h @@ -222,6 +222,7 @@ public: MWindow *mwindow; SetFormatThread *thread; + SetChannelsReset *channels_reset; SetChannelsCanvas *canvas; // Screen size width, height ScaleSizeText* dimension[2]; diff --git a/cinelerra-5.1/cinelerra/trackcanvas.C b/cinelerra-5.1/cinelerra/trackcanvas.C index 27eede52..748b8b2b 100644 --- a/cinelerra-5.1/cinelerra/trackcanvas.C +++ b/cinelerra-5.1/cinelerra/trackcanvas.C @@ -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; diff --git a/cinelerra-5.1/cinelerra/transitionpopup.C b/cinelerra-5.1/cinelerra/transitionpopup.C index dd834d56..115a2f1d 100644 --- a/cinelerra-5.1/cinelerra/transitionpopup.C +++ b/cinelerra-5.1/cinelerra/transitionpopup.C @@ -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;