3 * Copyright (C) 2008-2015 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "mixersalign.h"
29 #include "edlsession.h"
30 #include "localsession.h"
31 #include "mainerror.h"
32 #include "mainprogress.h"
34 #include "mwindowgui.h"
37 #include "preferences.h"
40 #include "renderengine.h"
43 #include "transportque.h"
46 // c = corr(a,b): A=fft(a),B=fft(b) C=A*conj(B) c=ifft(C)
47 static inline void conj_product(int n, double *rp, double *ip,
48 double *arp, double *aip, double *brp, double *bip)
50 for( int i=0; i<n; ++i ) {
51 double ar = arp[i], ai = aip[i];
52 double br = brp[i], bi = -bip[i];
53 rp[i] = ar*br - ai*bi;
54 ip[i] = ar*bi + ai*br;
58 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
66 MixersAlignMixer::~MixersAlignMixer()
72 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
73 N_("Mixer"), N_("Nudge"),
76 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
80 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
81 MixersAlign *dialog, int x, int y, int w, int h)
82 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
84 set_selection_mode(LISTBOX_MULTIPLE);
85 this->dialog = dialog;
87 for( int i=MIX_SZ; --i>=0; ) {
88 col_widths[i] = xS(mix_widths[i]);
89 col_titles[i] = _(mix_titles[i]);
93 MixersAlignMixerList::~MixersAlignMixerList()
98 void MixersAlignMixerList::clear()
100 for( int i=MIX_SZ; --i>=0; )
101 cols[i].remove_all_objects();
104 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
106 char mixer_text[BCSTRLEN];
107 snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
108 mixer->mixer->idx, mixer->mixer->title);
109 cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
110 char nudge_text[BCSTRLEN];
111 sprintf(nudge_text, _("%0.4f"), mixer->nudge);
112 cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
115 void MixersAlignMixerList::load_list()
118 for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
119 add_mixer(dialog->mixers[i]);
122 void MixersAlignMixerList::update()
124 int xpos = get_xposition(), ypos = get_yposition();
125 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
128 int MixersAlignMixerList::selection_changed()
130 for( int m=0; m<dialog->mixers.size(); ++m ) {
131 MixersAlignMixer *mix = dialog->mixers[m];
132 if( !is_selected(m) ) {
133 for( int i=0; i<dialog->atracks.size(); ++i ) {
134 if( m != dialog->amixer_of(i) ) continue;
135 gui->atrack_list->set_selected(i, 0);
142 else if( mix->aidx < 0 ) {
143 MixersAlignATrack *best = 0; int idx = -1;
144 for( int i=0; i<dialog->atracks.size(); ++i ) {
145 if( m != dialog->amixer_of(i) ) continue;
146 MixersAlignATrack *atrk = dialog->atracks[i];
147 if( atrk->mi < 0 ) continue;
148 gui->atrack_list->set_selected(i, 0);
149 if( best && best->mx >= atrk->mx ) continue;
150 best = atrk; idx = i;
152 if( idx >= 0 && best ) {
153 gui->atrack_list->set_selected(idx, 1);
154 MixersAlignMixer *mix = dialog->mixers[m];
158 mix->nudge = best->nudge;
161 for( int i=0; i<dialog->atracks.size(); ++i ) {
162 if( m != dialog->amixer_of(i) ) continue;
163 gui->atrack_list->set_selected(i, 1);
168 gui->atrack_list->update();
171 for( int i=0; i<dialog->atracks.size(); ++i ) {
172 if( !gui->atrack_list->is_selected(i) ) continue;
173 int m = dialog->amixer_of(i);
174 if( m >= 0 ) set_selected(m, 1);
180 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
187 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
188 N_("No"), N_("Mixer"), N_("Track"),
191 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
195 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
196 MixersAlign *dialog, int x, int y, int w, int h)
197 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
199 set_selection_mode(LISTBOX_SINGLE);
200 this->dialog = dialog;
202 for( int i=MTK_SZ; --i>=0; ) {
203 col_widths[i] = xS(mtk_widths[i]);
204 col_titles[i] = _(mtk_titles[i]);
208 MixersAlignMTrackList::~MixersAlignMTrackList()
213 void MixersAlignMTrackList::clear()
215 for( int i=MTK_SZ; --i>=0; )
216 cols[i].remove_all_objects();
219 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
221 char no_text[BCSTRLEN];
222 snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
223 cols[MTK_NO].append(new BC_ListBoxItem(no_text));
224 char mixer_text[BCSTRLEN];
225 Track *track = mtrk->track;
226 int k = -1, m = dialog->mixer_of(track, k);
227 snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,k);
228 cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
229 cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
232 void MixersAlignMTrackList::load_list()
235 for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
236 add_mtrack(dialog->mtracks[i]);
239 void MixersAlignMTrackList::update()
241 int xpos = get_xposition(), ypos = get_yposition();
242 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
246 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
255 MixersAlignATrack::~MixersAlignATrack()
260 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
261 N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
264 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
268 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
269 MixersAlign *dialog, int x, int y, int w, int h)
270 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
272 set_selection_mode(LISTBOX_MULTIPLE);
273 this->dialog = dialog;
275 for( int i=ATK_SZ; --i>=0; ) {
276 col_widths[i] = xS(atk_widths[i]);
277 col_titles[i] = _(atk_titles[i]);
281 MixersAlignATrackList::~MixersAlignATrackList()
286 void MixersAlignATrackList::clear()
288 for( int i=ATK_SZ; --i>=0; )
289 cols[i].remove_all_objects();
292 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
294 char atrack_text[BCSTRLEN];
295 Track *track = atrack->track;
296 int m = dialog->mixer_of(track);
297 snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
298 cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
299 cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
300 char nudge_text[BCSTRLEN];
301 sprintf(nudge_text, "%0.4f", atrack->nudge);
302 cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
303 char mx_text[BCSTRLEN];
304 sprintf(mx_text, "%0.4f", atrack->mx);
305 cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
306 char mi_text[BCSTRLEN];
307 sprintf(mi_text, "%jd", atrack->mi);
308 cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
312 void MixersAlignATrackList::load_list()
315 for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
316 add_atrack(dialog->atracks[i]);
319 void MixersAlignATrackList::update()
321 int xpos = get_xposition(), ypos = get_yposition();
322 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
325 int MixersAlignATrackList::selection_changed()
327 int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
328 int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
331 MixersAlignMixer *mix = dialog->mixers[m];
332 for( int i=0; i<dialog->atracks.size(); ++i ) {
333 int k = dialog->amixer_of(i);
334 if( k < 0 ) { set_selected(i, 0); continue; }
335 if( m != k ) continue;
336 MixersAlignATrack *atrk = dialog->atracks[i];
337 if( atrk->mi < 0 ) continue;
338 int is_sel = is_selected(i);
340 if( i != idx ) continue;
345 mix->nudge = atrk->nudge;
356 set_selected(idx, 1);
360 gui->mixer_list->load_list();
361 for( int i=0; i<dialog->atracks.size(); ++i ) {
362 if( !is_selected(i) ) continue;
363 int m = dialog->amixer_of(i);
364 if( m < 0 ) continue;
365 gui->mixer_list->set_selected(m, 1);
367 gui->mixer_list->update();
371 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
372 MixersAlign *dialog, int x, int y)
373 : BC_GenericButton(x, y, _("Reset"))
376 this->dialog = dialog;
379 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
381 return BC_GenericButton::calculate_w(gui, _("Reset"));
384 int MixersAlignReset::handle_event()
386 dialog->load_mixers();
387 dialog->load_mtracks();
388 dialog->load_atracks();
390 gui->default_selection();
394 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
395 MixersAlign *dialog, int x, int y)
396 : BC_GenericButton(x, y, _("Match"))
399 this->dialog = dialog;
402 int MixersAlignMatch::handle_event()
404 if( !dialog->thread->running() ) {
405 dialog->thread->start(1);
410 MixersAlignMatchAll::MixersAlignMatchAll(MixersAlignWindow *gui,
411 MixersAlign *dialog, int x, int y)
412 : BC_GenericButton(x, y, _("Match All"))
415 this->dialog = dialog;
418 int MixersAlignMatchAll::handle_event()
420 if( !dialog->thread->running() ) {
421 dialog->thread->start(0);
426 MixersAlignNudgeTracks::MixersAlignNudgeTracks(MixersAlignWindow *gui,
427 MixersAlign *dialog, int x, int y)
428 : BC_GenericButton(x, y, _("Apply"))
431 this->dialog = dialog;
434 int MixersAlignNudgeTracks::calculate_width(BC_WindowBase *gui)
436 return BC_GenericButton::calculate_w(gui, _("Apply"));
439 int MixersAlignNudgeTracks::handle_event()
441 dialog->nudge_tracks();
445 MixersAlignNudgeSelected::MixersAlignNudgeSelected(MixersAlignWindow *gui,
446 MixersAlign *dialog, int x, int y)
447 : BC_GenericButton(x, y, _("Move"))
450 this->dialog = dialog;
453 int MixersAlignNudgeSelected::calculate_width(BC_WindowBase *gui)
455 return BC_GenericButton::calculate_w(gui, _("Move"));
458 int MixersAlignNudgeSelected::handle_event()
460 dialog->nudge_selected();
464 MixersAlignUndoItem::MixersAlignUndoItem(const char *text, int no)
469 MixersAlignUndoItem::~MixersAlignUndoItem()
473 int MixersAlignUndoItem::handle_event()
475 MixersAlignUndo *undo = (MixersAlignUndo *)get_popup_menu();
476 undo->dialog->apply_undo(no);
480 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
481 MixersAlign *dialog, int x, int y)
482 : BC_PopupMenu(x, y, xS(100), _("Undo"))
485 this->dialog = dialog;
487 MixersAlignUndo::~MixersAlignUndo()
491 void MixersAlignUndo::create_objects()
496 void MixersAlignUndo::add_undo_item(int no)
500 sprintf(text, _("chkpt %d"), no);
502 sprintf(text, _("start over"));
503 add_item(new MixersAlignUndoItem(text, no));
506 MixersAlignCheckPoint::MixersAlignCheckPoint(MixersAlignWindow *gui,
507 MixersAlign *dialog, int x, int y)
508 : BC_GenericButton(x, y, xS(100), _("CheckPoint"))
511 this->dialog = dialog;
514 int MixersAlignCheckPoint::handle_event()
516 dialog->check_point();
520 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
521 : BC_Window(_("Align Mixers"), x, y, xS(880), yS(380), xS(880), yS(380), 1)
523 this->dialog = dialog;
524 // *** CONTEXT_HELP ***
525 context_help_set_keyword("Video sync using Waveforms");
527 MixersAlignWindow::~MixersAlignWindow()
531 void MixersAlignWindow::create_objects()
533 int xs10 = xS(10), xs20 = xS(20);
534 int ys10 = yS(10), ys20 = yS(20);
535 int x = xs10, y = ys10, w4 = (get_w()-x-xs10)/4, lw = w4 + xs20;
536 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
537 mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
538 add_subwindow(mixer_title);
539 mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
540 add_subwindow(mtrack_title);
541 atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
542 add_subwindow(atrack_title);
543 y += mixer_title->get_h() + ys10;
544 int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - yS(32);
546 mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-xs20, lh);
547 add_subwindow(mixer_list);
548 mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-xs20, lh);
549 add_subwindow(mtrack_list);
550 atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-xs20, lh);
551 add_subwindow(atrack_list);
552 int xr = x2-xs10 - MixersAlignReset::calculate_width(this);
554 add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y1));
555 add_subwindow(match = new MixersAlignMatch(this, dialog, x2+xs10, y1));
556 int xa = x3-xs10 - MixersAlignNudgeTracks::calculate_width(this);
557 add_subwindow(nudge_tracks = new MixersAlignNudgeTracks(this, dialog, xa, y1));
558 y2 = y1 + nudge_tracks->get_h() + ys10;
559 add_subwindow(match_all = new MixersAlignMatchAll(this, dialog, xr, y2));
560 add_subwindow(nudge_selected = new MixersAlignNudgeSelected(this, dialog, xa, y2));
562 add_subwindow(check_point = new MixersAlignCheckPoint(this, dialog, xu, y1));
563 add_subwindow(undo = new MixersAlignUndo(this, dialog, xu, y2));
564 undo->create_objects();
566 add_subwindow(new BC_OKButton(this));
567 add_subwindow(new BC_CancelButton(this));
570 int MixersAlignWindow::resize_event(int w, int h)
572 int xs10 = xS(10), xs20 = xS(20);
573 int ys10 = yS(10), ys20 = yS(20);
574 int x = xs10, y = ys10, w4 = (w-x-xs10)/4, lw = w4 + xs20;
575 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
576 mixer_title->reposition_window(x1, y);
577 mtrack_title->reposition_window(x2, y);
578 atrack_title->reposition_window(x3, y);
579 y += mixer_title->get_h() + ys10;
580 int y1 = y, y2 = h - BC_OKButton::calculate_h() - yS(32);
582 mixer_list->reposition_window(x1, y, x2-x1-xs20, lh);
583 mtrack_list->reposition_window(x2, y, x3-x2-xs20, lh);
584 atrack_list->reposition_window(x3, y, x4-x3-xs20, lh);
585 int xr = x2-xs10 - MixersAlignReset::calculate_width(this);
587 reset->reposition_window(xr, y1);
588 match->reposition_window(x2+xs10, y1);
589 int xa = x3-xs10 - MixersAlignNudgeTracks::calculate_width(this);
590 nudge_tracks->reposition_window(xa, y1);
591 y2 = y1 + nudge_tracks->get_h() + ys10;
592 match_all->reposition_window(xr, y2);
593 nudge_selected->reposition_window(xa, y2);
595 check_point->reposition_window(xu, y1);
596 undo->reposition_window(xu, y2);
600 void MixersAlignWindow::load_lists()
602 mixer_list->load_list();
603 mtrack_list->load_list();
604 atrack_list->load_list();
607 void MixersAlignWindow::default_selection()
609 // mixers selects all mixers
610 mixer_list->set_all_selected(1);
611 // master selects first mixer audio track
612 for( int i=0; i<dialog->mtracks.size(); ++i ) {
613 if( dialog->mmixer_of(i) >= 0 ) {
614 mtrack_list->set_selected(i, 1);
618 // audio selects all mixer audio tracks
619 for( int i=0; i<dialog->atracks.size(); ++i ) {
620 if( dialog->amixer_of(i) >= 0 )
621 atrack_list->set_selected(i, 1);
626 void MixersAlignWindow::update_gui()
628 mixer_list->update();
629 mtrack_list->update();
630 atrack_list->update();
634 MixersAlign::MixersAlign(MWindow *mwindow)
636 this->mwindow = mwindow;
638 farming = new Mutex("MixersAlign::farming");
639 total_lock = new Mutex("MixersAlign::total_lock");
640 thread = new MixersAlignThread(this);
645 sample_len = 0x10000;
648 master_start = master_end = 0;
649 audio_start = audio_end = 0;
652 MixersAlign::~MixersAlign()
655 farming->lock("MixersAlign::~MixersAlign");
665 void MixersAlign::start_dialog(int wx, int wy)
669 EDL *start_over = new EDL();
670 start_over->create_objects();
671 start_over->copy_all(mwindow->edl);
672 undo_edls.append(start_over);
676 BC_Window *MixersAlign::new_gui()
681 ma_gui = new MixersAlignWindow(this, wx, wy);
682 ma_gui->lock_window("MixersAlign::new_gui");
683 ma_gui->create_objects();
684 ma_gui->load_lists();
685 ma_gui->default_selection();
686 ma_gui->show_window(1);
687 ma_gui->unlock_window();
691 // shift armed mixer tracks by nudge
692 void MixersAlign::nudge_tracks()
694 mwindow->gui->lock_window("MixersAlign::apply_tracks");
695 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
696 int midx = mmixer_of(idx);
697 EDL *edl = mwindow->edl;
699 for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
700 if( m == midx ) continue; // master does not move
701 MixersAlignMixer *mix = mixers[m];
702 Mixer *mixer = mix->mixer;
703 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
704 int id = mixer->mixer_ids[i];
705 Track *track = edl->tracks->first;
706 while( track && track->mixer_id != id ) track = track->next;
707 if( !track ) continue;
708 double nudge = mix->nudge;
709 int armed = track->armed; track->armed = 1;
711 track->clear(0, -nudge, 1,
712 edl->session->labels_follow_edits,
713 edl->session->plugins_follow_edits,
714 edl->session->autos_follow_edits, 0);
716 else if( nudge > 0 ) {
717 track->paste_silence(0, nudge,
718 edl->session->plugins_follow_edits,
719 edl->session->autos_follow_edits);
721 track->armed = armed;
726 mwindow->update_gui(1);
727 mwindow->gui->unlock_window();
731 // move selected mixer edits by nudge
732 void MixersAlign::nudge_selected()
734 mwindow->gui->lock_window("MixersAlign::apply_selected");
735 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
736 int midx = mmixer_of(idx);
737 EDL *edl = mwindow->edl;
739 ArrayList<int> track_arms; // ugly
740 for( Track *track=edl->tracks->first; track; track=track->next ) {
741 track_arms.append(track->armed);
744 for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
745 if( m == midx ) continue; // master does not move
746 MixersAlignMixer *mix = mixers[m];
747 Mixer *mixer = mix->mixer;
748 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
749 int id = mixer->mixer_ids[i];
750 Track *track = edl->tracks->first;
751 while( track && track->mixer_id != id ) track = track->next;
752 if( !track ) continue;
753 double nudge = mix->nudge;
755 double position = 0; Track *first_track = 0;
756 EDL *clip = edl->selected_edits_to_clip(0, &position, &first_track);
758 Track *clip_track = clip->tracks->first;
759 Track *edl_track = first_track;
760 while( clip_track && edl_track ) {
761 Edit *edit = clip_track->edits->first;
762 for( ; edit; edit=edit->next ) {
763 double start = clip_track->from_units(edit->startproject);
764 double end = clip_track->from_units(edit->startproject+edit->length);
765 start += position; end += position;
766 edl_track->clear(start, end, 1,
767 edl->session->labels_follow_edits,
768 edl->session->plugins_follow_edits,
769 edl->session->autos_follow_edits, 0);
770 edl_track->paste_silence(start, end,
771 edl->session->plugins_follow_edits,
772 edl->session->autos_follow_edits);
774 clip_track = clip_track->next;
775 edl_track = edl_track->next;
778 edl->paste_edits(clip, first_track, position, 1);
784 for( Track *track=edl->tracks->first; track; track=track->next )
785 track->armed = track_arms[i++];
788 mwindow->update_gui(1);
789 mwindow->gui->unlock_window();
794 void MixersAlign::clear_mixer_nudge()
796 // so pressing apply twice does not damage the result
797 for( int m=0; m<mixers.size(); ++m )
798 mixers[m]->nudge = 0;
799 ma_gui->mixer_list->load_list();
800 ma_gui->lock_window("MixersAlign::clear_mixer_nudge");
801 ma_gui->mixer_list->update();
802 ma_gui->unlock_window();
805 void MixersAlign::check_point()
807 mwindow->gui->lock_window("MixersAlign::check_point");
808 ma_gui->undo->add_undo_item(undo_edls.size());
809 EDL *undo_edl = new EDL();
810 undo_edl->create_objects();
811 undo_edl->copy_all(mwindow->edl);
812 undo_edls.append(undo_edl);
813 mwindow->gui->unlock_window();
817 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
820 this->dialog = dialog;
822 MixersAlignThread::~MixersAlignThread()
827 void MixersAlignThread::start(int fwd)
830 MixersAlignWindow *gui = dialog->ma_gui;
831 gui->reset->disable();
832 gui->match->disable();
833 gui->match_all->disable();
834 gui->nudge_tracks->disable();
835 gui->nudge_selected->disable();
836 gui->check_point->disable();
837 // gui->undo->disable();
841 void MixersAlignThread::run()
847 MixersAlignWindow *gui = dialog->ma_gui;
848 gui->lock_window("MixersAlignThread::run");
849 gui->reset->enable();
850 gui->match->enable();
851 gui->match_all->enable();
852 gui->nudge_tracks->enable();
853 gui->nudge_selected->enable();
854 gui->check_point->enable();
855 // gui->undo->enable();
856 gui->unlock_window();
860 void MixersAlign::handle_done_event(int result)
862 if( thread->running() ) {
867 mwindow->gui->lock_window("MixersAlign::handle_done_event");
868 EDL *edl = mwindow->edl;
869 mwindow->edl = undo_edls[0];
870 mwindow->undo_before();
872 mwindow->undo_after(_("align mixers"), LOAD_ALL);
873 mwindow->gui->unlock_window();
877 void MixersAlign::handle_close_event(int result)
886 void MixersAlign::load_mixers()
889 Mixers &edl_mixers = mwindow->edl->mixers;
890 for( int i=0; i<edl_mixers.size(); ++i )
891 mixers.append(new MixersAlignMixer(edl_mixers[i]));
894 void MixersAlign::load_mtracks()
897 Track *track=mwindow->edl->tracks->first;
898 for( int no=0; track; ++no, track=track->next ) {
899 if( track->data_type != TRACK_AUDIO ) continue;
900 mtracks.append(new MixersAlignMTrack(track, no));
904 void MixersAlign::load_atracks()
907 Track *track=mwindow->edl->tracks->first;
908 for( int no=0; track; ++no, track=track->next ) {
909 if( track->data_type != TRACK_AUDIO ) continue;
910 if( mixer_of(track) < 0 ) continue;
911 atracks.append(new MixersAlignATrack(track, no));
916 int MixersAlign::atrack_of(MixersAlignMixer *mix, int ch)
919 Mixer *mixer = mix->mixer;
920 for( int i=0,n=mixer->mixer_ids.size(); i<n; ++i ) {
921 int id = mixer->mixer_ids[i]; k = atracks.size();
922 while( --k >= 0 && atracks[k]->track->mixer_id != id );
923 if( k < 0 ) continue;
924 if( --ch < 0 ) break;
929 int MixersAlign::mixer_of(Track *track, int &midx)
931 int id = track->mixer_id, k = mixers.size(), idx = -1;
932 while( idx < 0 && --k >= 0 )
933 idx = mixers[k]->mixer->mixer_ids.number_of(id);
939 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
941 EDL *edl = new EDL(mwindow->edl);
942 edl->create_objects();
943 Track *track = mwindow->edl->tracks->first;
944 for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
945 int id = track->mixer_id;
946 if( track->data_type != TRACK_AUDIO ) continue;
947 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
948 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
949 mixer_audio->copy_settings(track);
950 mixer_audio->play = 1;
951 mixer_audio->edits->copy_from(track->edits);
952 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
953 autos[AUTOMATION_PAN]->default_auto;
954 pan_auto->values[ch++] = 1.0;
959 EDL *MixersAlign::mixer_master_clip(Track *track)
961 EDL *edl = new EDL(mwindow->edl);
962 edl->create_objects();
963 Track *master_audio = edl->tracks->add_audio_track(0, 0);
964 master_audio->copy_settings(track);
965 master_audio->play = 1;
966 master_audio->edits->copy_from(track->edits);
967 PanAuto* pan_auto = (PanAuto*)master_audio->automation->
968 autos[AUTOMATION_PAN]->default_auto;
969 pan_auto->values[0] = 1.0;
973 int64_t MixersAlign::mixer_tracks_total(int midx)
975 int64_t total_len = 0;
976 int64_t sample_rate = mwindow->edl->get_sample_rate();
978 for( int i=0; (m=ma_gui->mixer_list->get_selection_number(0, i))>=0 ; ++i ) {
979 if( m == midx ) continue;
980 Mixer *mixer = mixers[m]->mixer;
981 double render_end = 0;
982 Track *track = mwindow->edl->tracks->first;
983 for( ; track; track=track->next ) {
984 int id = track->mixer_id;
985 if( track->data_type != TRACK_AUDIO ) continue;
986 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
987 double track_end = track->get_length();
988 if( render_end < track_end ) render_end = track_end;
990 if( render_end > audio_end ) render_end = audio_end;
991 double len = render_end - audio_start;
992 if( len > 0 ) total_len += len * sample_rate;
997 void MixersAlign::apply_undo(int no)
999 if( thread->running() ) {
1003 mwindow->gui->lock_window("MixersAlignUndo::handle_event");
1004 EDL *undo_edl = undo_edls[no];
1005 mwindow->edl->copy_all(undo_edl);
1006 mwindow->update_gui(1);
1007 mwindow->gui->unlock_window();
1008 ma_gui->reset->handle_event();
1011 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
1012 : RenderEngine(0, mwindow->preferences, 0, 0)
1014 TransportCommand command(mwindow->preferences);
1015 command.command = NORMAL_FWD;
1016 command.get_edl()->copy_all(edl);
1017 command.change_type = CHANGE_ALL;
1018 command.realtime = 0;
1019 set_vcache(mwindow->video_cache);
1020 set_acache(mwindow->audio_cache);
1021 arm_command(&command);
1024 MixersAlignARender::~MixersAlignARender()
1028 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
1030 return arender ? arender->process_buffer(samples, len, pos) : -1;
1033 // scan mixer tracks for best target
1034 MixersAlignScanFarm::MixersAlignScanFarm(MixersAlign *dialog, int cpus, int n)
1035 : LoadServer(cpus, n)
1037 dialog->farming->lock("MixersAlignScanFarm::MixersAlignScanFarm");
1038 this->dialog = dialog;
1041 MixersAlignScanFarm::~MixersAlignScanFarm()
1043 dialog->farming->unlock();
1046 MixersAlignScanPackage::MixersAlignScanPackage(MixersAlignScanFarm *farm)
1050 MixersAlignScanPackage::~MixersAlignScanPackage()
1054 LoadPackage* MixersAlignScanFarm::new_package()
1056 return new MixersAlignScanPackage(this);
1059 void MixersAlignScanFarm::init_packages()
1061 int idx = dialog->ma_gui->mtrack_list->get_selection_number(0, 0);
1062 int midx = dialog->mmixer_of(idx);
1063 for( int i=0, k=0; i<get_total_packages(); ++k ) {
1064 int m = dialog->ma_gui->mixer_list->get_selection_number(0, k);
1065 if( m == midx ) continue;
1066 MixersAlignScanPackage *pkg = (MixersAlignScanPackage *)get_package(i++);
1067 pkg->mixer = dialog->mixers[m];
1071 MixersAlignScanClient::MixersAlignScanClient(MixersAlignScanFarm *farm)
1078 MixersAlignScanClient::~MixersAlignScanClient()
1082 LoadClient* MixersAlignScanFarm::new_client()
1084 return new MixersAlignScanClient(this);
1087 void MixersAlignScanClient::process_package(LoadPackage *package)
1089 MixersAlignScanFarm *farm = (MixersAlignScanFarm *)server;
1090 MixersAlign *dialog = farm->dialog;
1091 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1092 if( dialog->failed ) return;
1093 pkg = (MixersAlignScanPackage *)package;
1094 MixersAlignMixer *mix = pkg->mixer;
1096 EDL *edl = dialog->mixer_audio_clip(mix->mixer);
1097 MixersAlignARender audio(dialog->mwindow, edl);
1098 double start = edl->skip_silence(0);
1099 int channels = edl->get_audio_channels();
1100 int64_t sample_rate = edl->get_sample_rate();
1101 int64_t cur_pos = start * sample_rate;
1102 int64_t end_pos = edl->get_audio_samples();
1103 int len = dialog->sample_len, len2 = len/2;
1104 cur_pos &= ~(len2-1);
1105 if( cur_pos ) dialog->update_progress(cur_pos);
1108 Samples *samples[MAX_CHANNELS];
1109 for( int i=0; i<MAX_CHANNELS; ++i )
1110 samples[i] = i<channels ? new Samples(len2) : 0;
1111 int cpus = bmin(dialog->mwindow->preferences->processors, channels);
1112 MixersAlignTarget targ(channels, cpus, this, samples, len2);
1114 while( !ret && !dialog->failed && cur_pos < end_pos ) {
1116 int64_t nxt_pos = pos + len2;
1117 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1118 len1 = nxt_pos - cur_pos;
1119 ret = audio.render(samples, len1, pos);
1121 targ.process_packages();
1122 dialog->update_progress(len1);
1123 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1127 if( !ret && !dialog->failed ) {
1130 MixersAlignMixer *mix = pkg->mixer;
1131 MixersAlignTargetPackage *best_pkg = 0;
1132 for( int i=0,n=targ.get_total_packages(); i<n; ++i ) {
1133 MixersAlignTargetPackage *targ_pkg =
1134 (MixersAlignTargetPackage *) targ.get_package(i);
1135 if( sd2 >= targ_pkg->sd2 ) continue;
1136 sd2 = targ_pkg->sd2;
1137 int k = dialog->atrack_of(mix, i);
1138 if( k < 0 ) continue;
1139 MixersAlignATrack *atrk = dialog->atracks[k];
1140 atrk->mi = targ_pkg->pos;
1141 atrk->ss = targ_pkg->ss;
1142 idx = k; best_pkg = targ_pkg;
1145 mix->br = new double[len];
1146 mix->bi = new double[len];
1147 double *br = mix->br;
1148 double *bp = best_pkg->best;
1150 while( i < len2 ) br[i++] = *bp++;
1151 while( i < len ) br[i++] = 0;
1153 fft.do_fft(len, 0, mix->br, 0, mix->br, mix->bi);
1158 if( ret && !dialog->failed ) {
1159 eprintf("Audio render failed:\n%s", edl->path);
1163 for( int i=channels; --i>=0; ) delete samples[i];
1167 // scan mixer channels for best target
1168 MixersAlignTarget::MixersAlignTarget(int n, int cpus,
1169 MixersAlignScanClient *scan, Samples **samples, int len)
1170 : LoadServer(n, cpus)
1173 this->samples = samples;
1176 MixersAlignTarget::~MixersAlignTarget()
1180 MixersAlignTargetClient::MixersAlignTargetClient()
1184 MixersAlignTargetClient::~MixersAlignTargetClient()
1188 LoadClient* MixersAlignTarget::new_client()
1190 return new MixersAlignTargetClient();
1193 MixersAlignTargetPackage::MixersAlignTargetPackage(MixersAlignTarget *targ)
1197 best = new double[targ->len];
1199 MixersAlignTargetPackage::~MixersAlignTargetPackage()
1204 LoadPackage* MixersAlignTarget::new_package()
1206 return new MixersAlignTargetPackage(this);
1209 void MixersAlignTarget::init_packages()
1213 void MixersAlignTargetClient::process_package(LoadPackage *package)
1215 MixersAlignTarget *targ = (MixersAlignTarget *)server;
1216 MixersAlignScanClient *scan = targ->scan;
1217 MixersAlignScanFarm *farm = (MixersAlignScanFarm *)scan->server;
1218 MixersAlign *dialog = farm->dialog;
1219 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1220 if( dialog->failed ) return;
1221 pkg = (MixersAlignTargetPackage *)package;
1223 int ch = get_package_number();
1224 double *data = targ->samples[ch]->get_data();
1225 int len1 = scan->len1;
1226 // computes sum(s**2), sum(d2**2) d2=discrete 2nd deriv
1227 // d0=s[i+0]-s[i+1], d1=s[i+1]-s[i+2], d2=d0-d1
1228 // d = s[i+0] - 2*s[i+1] + s[i+2]
1229 double ss = 0, sd2 = 0;
1230 double a = 0, b = 0, c = 0;
1231 for( int i=0; i<len1; ++i ) {
1232 a = b; b = c; c = data[i];
1233 double d = a - 2*b + c;
1234 ss += c*c; sd2 += d*d;
1236 //best is highest sd2
1237 if( pkg->sd2 < sd2 ) {
1240 pkg->pos = scan->pos;
1241 //printf("targ %s:%d at %jd,ss=%f sd2=%f\n",
1242 // scan->pkg->mixer->mixer->title, ch, scan->pos, ss, sd2);
1243 double *best = pkg->best;
1244 int i = 0, len = targ->len;
1245 while( i < len1 ) best[i++] = *data++;
1246 while( i < len ) best[i++] = 0;
1250 void MixersAlign::scan_master(Track *track)
1252 EDL *edl = mixer_master_clip(track);
1253 MixersAlignARender audio(mwindow, edl);
1255 int channels = edl->get_audio_channels();
1256 int64_t sample_rate = edl->get_sample_rate();
1257 int64_t audio_samples = edl->get_audio_samples();
1258 int64_t cur_pos = audio_start * sample_rate;
1259 int64_t end_pos = audio_end * sample_rate;
1260 if( end_pos > audio_samples ) end_pos = audio_samples;
1261 if( cur_pos >= end_pos ) {
1262 eprintf(_("scan master track empty"));
1266 int len = sample_len, len2 = len/2;
1267 double *audio_r = new double[len];
1268 double *audio_i = new double[len];
1269 Samples *samples[2][MAX_CHANNELS];
1270 for( int k=0; k<2; ++k ) {
1271 for( int i=0; i<MAX_CHANNELS; ++i )
1272 samples[k][i] = i<channels ? new Samples(len2) : 0;
1276 for( int i=0,n=mixers.size(); i<n; ++i )
1277 if( mixers[i]->br ) ++m;
1278 int cpus = bmin(mwindow->preferences->processors, m);
1279 MixersAlignMatchRevFarm farm(m, cpus, this, audio_r, audio_i, len);
1283 start_progress(end_pos - cur_pos);
1286 int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1287 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1288 int len1 = nxt_pos - pos;
1289 int ret = audio.render(samples[k], len1, pos);
1290 while( !ret && !failed && cur_pos < end_pos ) {
1292 cur_pos = nxt_pos; nxt_pos += len2;
1293 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1294 len1 = nxt_pos - cur_pos;
1295 ret = audio.render(samples[1-k], len1, cur_pos);
1297 update_progress(len2);
1299 double *lp = samples[k][0]->get_data();
1300 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1301 double *np = samples[k=1-k][0]->get_data();
1302 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1303 while( i < len ) audio_r[i++] = 0;
1304 fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1306 farm.process_packages();
1307 if( progress->is_cancelled() ) failed = -1;
1310 if( ret && !failed ) {
1311 eprintf("Audio render failed:\n%s", edl->path);
1315 char text[BCSTRLEN];
1316 double secs = timer.get_difference()/1000.;
1317 sprintf(text, _("Match mixer done: %0.3f secs"), secs);
1318 stop_progress(text);
1322 for( int k=0; k<2; ++k ) {
1323 for( int i=channels; --i>=0; )
1324 delete samples[k][i];
1329 MixersAlignMatchFwdPackage::MixersAlignMatchFwdPackage()
1334 MixersAlignMatchFwdPackage::~MixersAlignMatchFwdPackage()
1338 MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm(MixersAlign *dialog, int n)
1339 : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
1341 this->dialog = dialog;
1342 dialog->farming->lock("MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm");
1344 MixersAlignMatchFwdFarm::~MixersAlignMatchFwdFarm()
1346 dialog->farming->unlock();
1349 LoadPackage* MixersAlignMatchFwdFarm::new_package()
1351 return new MixersAlignMatchFwdPackage();
1354 void MixersAlignMatchFwdFarm::init_packages()
1356 for( int i = 0; i < get_total_packages(); ++i ) {
1357 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
1358 MixersAlignMatchFwdPackage *package = (MixersAlignMatchFwdPackage *)get_package(i);
1359 package->mixer = dialog->mixers[m];
1363 LoadClient* MixersAlignMatchFwdFarm::new_client()
1365 return new MixersAlignMatchFwdClient(this);
1368 MixersAlignMatchFwdClient::MixersAlignMatchFwdClient(MixersAlignMatchFwdFarm *farm)
1373 MixersAlignMatchFwdClient::~MixersAlignMatchFwdClient()
1377 void MixersAlignMatchFwdClient::process_package(LoadPackage *package)
1379 MixersAlignMatchFwdFarm *farm = (MixersAlignMatchFwdFarm *)server;
1380 MixersAlign *dialog = farm->dialog;
1381 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1382 if( dialog->failed ) return;
1383 pkg = (MixersAlignMatchFwdPackage *)package;
1385 MixersAlignMixer *amix = pkg->mixer;
1386 EDL *edl = dialog->mixer_audio_clip(amix->mixer);
1387 MixersAlignARender audio(dialog->mwindow, edl);
1388 int channels = edl->get_audio_channels();
1389 int64_t sample_rate = edl->get_sample_rate();
1390 int64_t audio_samples = edl->get_audio_samples();
1391 int64_t cur_pos = dialog->audio_start * sample_rate;
1392 int64_t end_pos = dialog->audio_end * sample_rate;
1393 if( end_pos > audio_samples ) end_pos = audio_samples;
1394 int len = dialog->master_len, len2 = len/2;
1395 double *audio_r = new double[len];
1396 double *audio_i = new double[len];
1398 Samples *samples[2][MAX_CHANNELS];
1399 for( int k=0; k<2; ++k ) {
1400 for( int i=0; i<MAX_CHANNELS; ++i )
1401 samples[k][i] = i<channels ? new Samples(len2) : 0;
1406 int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1407 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1408 int len1 = nxt_pos - pos;
1409 int ret = audio.render(samples[k], len1, pos);
1410 while( !ret && !dialog->failed && cur_pos < end_pos ) {
1411 dialog->update_progress(len1);
1415 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1416 len1 = nxt_pos - cur_pos;
1417 ret = audio.render(samples[1-k], len1, cur_pos);
1419 Track *track = edl->tracks->first;
1420 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
1421 int id = track->mixer_id, atk = dialog->atracks.size();
1422 while( --atk >= 0 && id != dialog->atracks[atk]->track->mixer_id );
1423 if( atk < 0 ) continue;
1425 double *lp = samples[k][ch]->get_data();
1426 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1427 double *np = samples[1-k][ch]->get_data();
1428 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1429 while( i < len ) audio_r[i++] = 0;
1430 fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1431 conj_product(len, audio_r, audio_i, audio_r, audio_i,
1432 dialog->master_r, dialog->master_i);
1433 fft.do_fft(len, 1, audio_r, audio_i);
1434 double mx = 0; int64_t mi = -1;
1435 for( int i=0; i<len2; ++i ) {
1436 double v = fabs(audio_r[i]);
1437 if( mx < v ) { mx = v; mi = i + pos; }
1439 mx /= dialog->master_ss;
1440 MixersAlignATrack *atrack= dialog->atracks[atk];
1441 if( atrack->mx < mx ) {
1447 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1449 if( ret && !dialog->failed ) {
1450 eprintf("Audio render failed:\n%s", edl->path);
1455 for( int k=0; k<2; ++k ) {
1456 for( int i=channels; --i>=0; )
1457 delete samples[k][i];
1463 MixersAlignMatchRevFarm::MixersAlignMatchRevFarm(int n, int cpus,
1464 MixersAlign *dialog, double *ar, double *ai, int len)
1465 : LoadServer(n, cpus)
1467 this->dialog = dialog;
1471 mixer_lock = new Mutex("MixersAlignMatchRevFarm::mixer_lock");
1474 MixersAlignMatchRevFarm::~MixersAlignMatchRevFarm()
1479 MixersAlignMatchRevPackage::MixersAlignMatchRevPackage()
1483 MixersAlignMatchRevPackage::~MixersAlignMatchRevPackage()
1487 void MixersAlignMatchRevFarm::init_packages()
1489 for( int i=0,m=0,n=dialog->mixers.size(); m<n; ++m ) {
1490 if( !dialog->mixers[m]->br ) continue;
1491 MixersAlignMatchRevPackage *pkg = (MixersAlignMatchRevPackage *)get_package(i++);
1492 pkg->mix = dialog->mixers[m];
1496 LoadClient *MixersAlignMatchRevFarm::new_client()
1498 return new MixersAlignMatchRevClient(this);
1500 LoadPackage *MixersAlignMatchRevFarm::new_package()
1502 return new MixersAlignMatchRevPackage();
1505 void MixersAlignMatchRevClient::process_package(LoadPackage *package)
1507 MixersAlignMatchRevFarm *farm = (MixersAlignMatchRevFarm *)server;
1508 MixersAlign *dialog = farm->dialog;
1509 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1510 if( dialog->failed ) return;
1511 pkg = (MixersAlignMatchRevPackage *)package;
1512 MixersAlignMixer *mix = pkg->mix;
1513 if( mix->aidx < 0 ) return;
1514 int64_t ss = dialog->atracks[mix->aidx]->ss;
1516 conj_product(farm->len, re, im, farm->ar, farm->ai, mix->br, mix->bi);
1518 fft.do_fft(farm->len, 1, re, im);
1519 double mx = 0; int64_t mi = -1;
1520 for( int i=0,n=farm->len/2; i<n; ++i ) {
1521 double r = fabs(re[i]) / ss;
1527 farm->mixer_lock->lock("MixersAlignMatchRevFarm::process_package");
1528 if( mix->mx < mx ) {
1531 //printf("best %d: %f at %jd\n", get_package_number(), mx, mi);
1533 farm->mixer_lock->unlock();
1536 MixersAlignMatchRevClient::MixersAlignMatchRevClient(MixersAlignMatchRevFarm *farm)
1538 re = new double[farm->len];
1539 im = new double[farm->len];
1541 MixersAlignMatchRevClient::~MixersAlignMatchRevClient()
1547 void MixersAlign::start_progress(int64_t total_len)
1550 mwindow->gui->lock_window("MixersAlign::start_progress");
1551 progress = mwindow->mainprogress->
1552 start_progress(_("match mixer audio"), total_len);
1553 mwindow->gui->unlock_window();
1555 void MixersAlign::stop_progress(const char *msg)
1557 mwindow->gui->lock_window("MixersAlign::stop_progress");
1558 progress->update(0);
1559 mwindow->mainprogress->end_progress(progress);
1561 if( msg ) mwindow->gui->show_message(msg);
1562 mwindow->gui->unlock_window();
1565 void MixersAlign::reset_targets()
1567 for( int m=0,n=mixers.size(); m<n; ++m ) {
1568 MixersAlignMixer *mix = mixers[m];
1569 delete mix->br; mix->br = 0;
1570 delete mix->bi; mix->bi = 0;
1574 for( int i=0,n=atracks.size(); i<n; ++i ) {
1575 MixersAlignATrack *atrk = atracks[i];
1576 atrk->nudge = 0; atrk->ss = 0;
1577 atrk->mx = 0; atrk->mi = -1;
1581 void MixersAlign::scan_targets()
1583 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1584 int midx = mmixer_of(idx);
1585 int64_t total_len = mixer_tracks_total(midx);
1586 start_progress(total_len);
1587 int m = mixers.size();
1588 if( midx >= 0 ) --m;
1589 int cpus = bmin(mwindow->preferences->processors, m);
1590 MixersAlignScanFarm scan(this, cpus, m);
1591 scan.process_packages();
1595 void MixersAlign::update_progress(int64_t len)
1598 total_rendered += len;
1599 total_lock->unlock();
1600 progress->update(total_rendered);
1603 static inline uint64_t high_bit_mask(uint64_t n)
1605 n |= n >> 1; n |= n >> 2;
1606 n |= n >> 4; n |= n >> 8;
1607 n |= n >> 16; n |= n >> 32;
1611 void MixersAlign::load_master_audio(Track *track)
1613 EDL *edl = mixer_master_clip(track);
1614 MixersAlignARender audio(mwindow, edl);
1615 int channels = edl->get_audio_channels();
1616 int64_t sample_rate = edl->get_sample_rate();
1617 int64_t audio_samples = edl->get_audio_samples();
1618 int64_t cur_pos = master_start * sample_rate;
1619 int64_t end_pos = master_end * sample_rate;
1620 if( end_pos > audio_samples ) end_pos = audio_samples;
1621 if( cur_pos >= end_pos ) {
1622 eprintf(_("master audio track empty"));
1626 int64_t audio_len = end_pos - cur_pos;
1627 if( audio_len > sample_rate * 60 ) {
1628 eprintf(_("master audio track length > 60 seconds"));
1632 int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
1633 if( master_len != fft_len ) {
1634 master_len = fft_len;
1635 delete [] master_r; master_r = new double[master_len];
1636 delete [] master_i; master_i = new double[master_len];
1639 Samples *samples[MAX_CHANNELS];
1640 for( int i=0; i<MAX_CHANNELS; ++i )
1641 samples[i] = i<channels ? new Samples(audio_len) : 0;
1642 int ret = audio.render(samples, audio_len, cur_pos);
1643 if( ret ) failed = 1;
1646 double *dp = samples[0]->get_data();
1647 int i = 0; double ss = 0;
1648 for( ; i<audio_len; ++i ) { ss += *dp * *dp; master_r[i] = *dp++; }
1650 for( ; i<fft_len; ++i ) master_r[i] = 0;
1651 for( int i=channels; --i>=0; ) delete samples[i];
1653 do_fft(fft_len, 0, master_r, 0, master_r, master_i);
1656 void MixersAlign::scan_mixer_audio()
1658 for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
1660 while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
1662 eprintf(_("no mixers selected"));
1667 MixersAlignMatchFwdFarm farm(this, n);
1668 int64_t total_len = mixer_tracks_total(-1);
1669 start_progress(total_len);
1671 farm.process_packages();
1672 if( progress->is_cancelled() ) failed = -1;
1674 char text[BCSTRLEN];
1675 double secs = timer.get_difference()/1000.;
1676 sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1677 stop_progress(text);
1680 void MixersAlign::match_fwd()
1682 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1684 eprintf(_("selection (master) not set"));
1687 master_start = mwindow->edl->local_session->get_inpoint();
1688 if( master_start < 0 ) {
1689 eprintf(_("in point selection (master start) must be set"));
1692 master_end = mwindow->edl->local_session->get_outpoint();
1693 if( master_end < 0 ) {
1694 eprintf(_("out point selection (master end) must be set"));
1697 if( master_start >= master_end ) {
1698 eprintf(_("in/out point selection (master start/end) invalid"));
1701 audio_start = mwindow->edl->local_session->get_selectionstart();
1702 audio_end = mwindow->edl->local_session->get_selectionend();
1703 if( audio_start >= audio_end ) {
1704 eprintf(_("selection (audio start/end) invalid"));
1710 load_master_audio(mtracks[idx]->track);
1716 mwindow->gui->lock_window("MixersAlign::update_match_fwd");
1717 mwindow->gui->show_message(_("mixer selection match canceled"));
1718 mwindow->gui->unlock_window();
1720 else if( failed > 0 )
1721 eprintf(_("Error in match render."));
1724 void MixersAlign::match_rev()
1726 int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1728 eprintf(_("selection (master) not set"));
1731 Track *track = mtracks[midx]->track;
1732 audio_start = mwindow->edl->local_session->get_selectionstart();
1733 audio_end = mwindow->edl->local_session->get_selectionend();
1734 if( audio_start >= audio_end ) {
1735 eprintf(_("selection (audio start/end) invalid"));
1750 mwindow->gui->lock_window("MixersAlign::update_match_rev");
1751 mwindow->gui->show_message(_("mixer selection match canceled"));
1752 mwindow->gui->unlock_window();
1754 else if( failed > 0 )
1755 eprintf(_("Error in match render."));
1758 void MixersAlign::update_fwd()
1760 double sample_rate = mwindow->edl->get_sample_rate();
1761 int64_t mi = master_start * sample_rate;
1762 // mixer best matches
1763 for( int m=0; m<mixers.size(); ++m ) {
1764 MixersAlignMixer *mix = mixers[m];
1765 Mixer *mixer = mix->mixer;
1766 mix->mx = 0; mix->mi = -1; mix->aidx = -1;
1767 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1768 int id = mixer->mixer_ids[i], k = atracks.size();
1769 while( --k >= 0 && atracks[k]->track->mixer_id != id );
1770 if( k < 0 ) continue;
1771 MixersAlignATrack *atrk = atracks[k];
1772 atrk->nudge = atrk->mi < 0 ? -1 :
1773 (mi - atrk->mi) / sample_rate;
1774 if( mix->mx >= atrk->mx ) continue;
1778 mix->nudge = atrk->nudge;
1784 void MixersAlign::update_rev()
1786 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1787 int midx = mmixer_of(idx);
1788 double sample_rate = mwindow->edl->get_sample_rate();
1789 for( int m=0,n=mixers.size(); m<n; ++m ) {
1790 if( m == midx ) continue;
1791 if( !ma_gui->mixer_list->is_selected(m) ) continue;
1792 MixersAlignMixer *mix = mixers[m];
1793 if( mix->aidx < 0 ) continue;
1794 MixersAlignATrack *atrk = atracks[mix->aidx];
1795 mix->nudge = atrk->mi < 0 ? -1 :
1796 (mix->mi - atrk->mi) / sample_rate;
1802 void MixersAlign::update()
1804 ma_gui->lock_window("MixersAlign::update");
1805 ma_gui->mixer_list->load_list();
1806 ma_gui->mixer_list->set_all_selected(1);
1807 ma_gui->mixer_list->update();
1809 ma_gui->atrack_list->load_list();
1810 for( int m=0; m<mixers.size(); ++m ) {
1811 int aidx = mixers[m]->aidx;
1812 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1814 ma_gui->atrack_list->update();
1815 ma_gui->unlock_window();