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"
27 #include "localsession.h"
28 #include "mainerror.h"
29 #include "mainprogress.h"
31 #include "mwindowgui.h"
32 #include "preferences.h"
35 #include "renderengine.h"
38 #include "transportque.h"
41 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
49 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
50 N_("Mixer"), N_("Nudge"),
53 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
57 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
58 MixersAlign *dialog, int x, int y, int w, int h)
59 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
61 set_selection_mode(LISTBOX_MULTIPLE);
62 this->dialog = dialog;
64 for( int i=MIX_SZ; --i>=0; ) {
65 col_widths[i] = mix_widths[i];
66 col_titles[i] = _(mix_titles[i]);
70 MixersAlignMixerList::~MixersAlignMixerList()
75 void MixersAlignMixerList::clear()
77 for( int i=MIX_SZ; --i>=0; )
78 cols[i].remove_all_objects();
81 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
83 char mixer_text[BCSTRLEN];
84 snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
85 mixer->mixer->idx, mixer->mixer->title);
86 cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
87 char nudge_text[BCSTRLEN];
88 sprintf(nudge_text, _("%0.4f"), mixer->nudge);
89 cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
92 void MixersAlignMixerList::load_list()
95 for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
96 add_mixer(dialog->mixers[i]);
99 void MixersAlignMixerList::update()
101 int xpos = get_xposition(), ypos = get_yposition();
102 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
105 int MixersAlignMixerList::selection_changed()
107 for( int m=0; m<dialog->mixers.size(); ++m ) {
108 MixersAlignMixer *mix = dialog->mixers[m];
109 if( !is_selected(m) ) {
110 for( int i=0; i<dialog->atracks.size(); ++i ) {
111 if( m != dialog->amixer_of(i) ) continue;
112 gui->atrack_list->set_selected(i, 0);
119 else if( mix->aidx < 0 ) {
120 MixersAlignATrack *best = 0; int idx = -1;
121 for( int i=0; i<dialog->atracks.size(); ++i ) {
122 if( m != dialog->amixer_of(i) ) continue;
123 MixersAlignATrack *atrk = dialog->atracks[i];
124 if( atrk->mi < 0 ) continue;
125 gui->atrack_list->set_selected(i, 0);
126 if( best && best->mx >= atrk->mx ) continue;
127 best = atrk; idx = i;
129 if( idx >= 0 && best ) {
130 gui->atrack_list->set_selected(idx, 1);
131 MixersAlignMixer *mix = dialog->mixers[m];
135 mix->nudge = best->nudge;
138 for( int i=0; i<dialog->atracks.size(); ++i ) {
139 if( m != dialog->amixer_of(i) ) continue;
140 gui->atrack_list->set_selected(i, 1);
145 gui->atrack_list->update();
148 for( int i=0; i<dialog->atracks.size(); ++i ) {
149 if( !gui->atrack_list->is_selected(i) ) continue;
150 int m = dialog->amixer_of(i);
151 if( m >= 0 ) set_selected(m, 1);
157 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
164 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
165 N_("No"), N_("Mixer"), N_("Track"),
168 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
172 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
173 MixersAlign *dialog, int x, int y, int w, int h)
174 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
176 set_selection_mode(LISTBOX_SINGLE);
177 this->dialog = dialog;
179 for( int i=MTK_SZ; --i>=0; ) {
180 col_widths[i] = mtk_widths[i];
181 col_titles[i] = _(mtk_titles[i]);
185 MixersAlignMTrackList::~MixersAlignMTrackList()
190 void MixersAlignMTrackList::clear()
192 for( int i=MTK_SZ; --i>=0; )
193 cols[i].remove_all_objects();
196 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
198 char no_text[BCSTRLEN];
199 snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
200 cols[MTK_NO].append(new BC_ListBoxItem(no_text));
201 char mixer_text[BCSTRLEN];
202 Track *track = mtrk->track;
203 int midx = -1, m = dialog->mixer_of(track, midx);
204 snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,midx);
205 cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
206 cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
209 void MixersAlignMTrackList::load_list()
212 for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
213 add_mtrack(dialog->mtracks[i]);
216 void MixersAlignMTrackList::update()
218 int xpos = get_xposition(), ypos = get_yposition();
219 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
223 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
232 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
233 N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
236 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
240 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
241 MixersAlign *dialog, int x, int y, int w, int h)
242 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
244 set_selection_mode(LISTBOX_MULTIPLE);
245 this->dialog = dialog;
247 for( int i=ATK_SZ; --i>=0; ) {
248 col_widths[i] = atk_widths[i];
249 col_titles[i] = _(atk_titles[i]);
253 MixersAlignATrackList::~MixersAlignATrackList()
258 void MixersAlignATrackList::clear()
260 for( int i=ATK_SZ; --i>=0; )
261 cols[i].remove_all_objects();
264 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
266 char atrack_text[BCSTRLEN];
267 Track *track = atrack->track;
268 int midx = -1, m = dialog->mixer_of(track, midx);
269 snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
270 cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
271 cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
272 char nudge_text[BCSTRLEN];
273 sprintf(nudge_text, "%0.4f", atrack->nudge);
274 cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
275 char mx_text[BCSTRLEN];
276 sprintf(mx_text, "%0.4f", atrack->mx);
277 cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
278 char mi_text[BCSTRLEN];
279 sprintf(mi_text, "%jd", atrack->mi);
280 cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
284 void MixersAlignATrackList::load_list()
287 for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
288 add_atrack(dialog->atracks[i]);
291 void MixersAlignATrackList::update()
293 int xpos = get_xposition(), ypos = get_yposition();
294 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
297 int MixersAlignATrackList::selection_changed()
299 int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
300 int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
303 MixersAlignMixer *mix = dialog->mixers[m];
304 for( int i=0; i<dialog->atracks.size(); ++i ) {
305 int k = dialog->amixer_of(i);
306 if( k < 0 ) { set_selected(i, 0); continue; }
307 if( m != k ) continue;
308 MixersAlignATrack *atrk = dialog->atracks[i];
309 if( atrk->mi < 0 ) continue;
310 int is_sel = is_selected(i);
312 if( i != idx ) continue;
317 mix->nudge = atrk->nudge;
328 set_selected(idx, 1);
332 gui->mixer_list->load_list();
333 for( int i=0; i<dialog->atracks.size(); ++i ) {
334 if( !is_selected(i) ) continue;
335 int m = dialog->amixer_of(i);
336 if( m < 0 ) continue;
337 gui->mixer_list->set_selected(m, 1);
339 gui->mixer_list->update();
343 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
344 MixersAlign *dialog, int x, int y)
345 : BC_GenericButton(x, y, _("Reset"))
348 this->dialog = dialog;
351 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
353 return BC_GenericButton::calculate_w(gui, _("Reset"));
356 int MixersAlignReset::handle_event()
358 dialog->load_mixers();
359 dialog->load_mtracks();
360 dialog->load_atracks();
362 gui->default_selection();
366 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
367 MixersAlign *dialog, int x, int y)
368 : BC_GenericButton(x, y, _("Match"))
371 this->dialog = dialog;
374 int MixersAlignMatch::handle_event()
376 if( !dialog->thread->running() ) {
377 gui->reset->disable();
378 gui->match->disable();
379 gui->apply->disable();
380 gui->undo->disable();
381 dialog->thread->start();
386 MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
387 MixersAlign *dialog, int x, int y)
388 : BC_GenericButton(x, y, _("Apply"))
391 this->dialog = dialog;
394 int MixersAlignApply::calculate_width(BC_WindowBase *gui)
396 return BC_GenericButton::calculate_w(gui, _("Apply"));
399 int MixersAlignApply::handle_event()
405 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
406 MixersAlign *dialog, int x, int y)
407 : BC_GenericButton(x, y, _("Undo"))
410 this->dialog = dialog;
413 int MixersAlignUndo::handle_event()
415 MWindow *mwindow = dialog->mwindow;
416 mwindow->edl->copy_all(dialog->undo_edl);
417 mwindow->gui->lock_window("MixersAlignUndo::handle_event");
418 mwindow->update_gui(1);
419 mwindow->gui->unlock_window();
420 return gui->reset->handle_event();
424 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
425 : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
427 this->dialog = dialog;
429 MixersAlignWindow::~MixersAlignWindow()
433 void MixersAlignWindow::create_objects()
435 int x = 10, y = 10, w4 = (get_w()-x-10)/4, lw = w4 + 20;
436 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
437 mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
438 add_subwindow(mixer_title);
439 mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
440 add_subwindow(mtrack_title);
441 atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
442 add_subwindow(atrack_title);
443 y += mixer_title->get_h() + 10;
444 int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 15;
446 mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
447 add_subwindow(mixer_list);
448 mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-20, lh);
449 add_subwindow(mtrack_list);
450 atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
451 add_subwindow(atrack_list);
452 int xr = x2-10 - MixersAlignReset::calculate_width(this);
453 add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y2+20));
454 add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y2+20));
455 int xa = x3-10 - MixersAlignApply::calculate_width(this);
456 add_subwindow(apply = new MixersAlignApply(this, dialog, xa, y2+20));
457 add_subwindow(undo = new MixersAlignUndo(this, dialog, x3+10, y2+20));
459 add_subwindow(new BC_OKButton(this));
460 add_subwindow(new BC_CancelButton(this));
463 int MixersAlignWindow::resize_event(int w, int h)
465 int x = 10, y = 10, w4 = (w-x-10)/4, lw = w4 + 20;
466 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
467 mixer_title->reposition_window(x1, y);
468 mtrack_title->reposition_window(x2, y);
469 atrack_title->reposition_window(x3, y);
470 y += mixer_title->get_h() + 10;
471 int y1 = y, y2 = h - BC_OKButton::calculate_h() - 15;
473 mixer_list->reposition_window(x1, y, x2-x1-20, lh);
474 mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
475 atrack_list->reposition_window(x3, y, x4-x3-20, lh);
476 int xr = x2-10 - MixersAlignReset::calculate_width(this);
477 reset->reposition_window(xr, y2+20);
478 match->reposition_window(x2+10, y2+20);
479 int xa = x3-10 - MixersAlignApply::calculate_width(this);
480 apply->reposition_window(xa, y2+20);
481 undo->reposition_window(x3+10, y2+20);
485 void MixersAlignWindow::load_lists()
487 mixer_list->load_list();
488 mtrack_list->load_list();
489 atrack_list->load_list();
492 void MixersAlignWindow::default_selection()
494 // mixers selects all mixers
495 mixer_list->set_all_selected(1);
496 // master selects first mixer audio track
497 for( int i=0; i<dialog->mtracks.size(); ++i ) {
498 if( dialog->mmixer_of(i) >= 0 ) {
499 mtrack_list->set_selected(i, 1);
503 // audio selects all mixer audio tracks
504 for( int i=0; i<dialog->atracks.size(); ++i ) {
505 if( dialog->amixer_of(i) >= 0 )
506 atrack_list->set_selected(i, 1);
511 void MixersAlignWindow::update_gui()
513 mixer_list->update();
514 mtrack_list->update();
515 atrack_list->update();
519 MixersAlign::MixersAlign(MWindow *mwindow)
521 this->mwindow = mwindow;
523 farming = new Mutex("MixersAlign::farming");
524 total_lock = new Mutex("MixersAlign::total_lock");
525 thread = new MixersAlignThread(this);
533 master_start = master_end = 0;
534 audio_start = audio_end = 0;
537 MixersAlign::~MixersAlign()
540 farming->lock("MixersAlign::~MixersAlign");
550 void MixersAlign::start_dialog(int wx, int wy)
554 this->undo_edl = new EDL();
555 undo_edl->create_objects();
556 undo_edl->copy_all(mwindow->edl);
560 BC_Window *MixersAlign::new_gui()
565 ma_gui = new MixersAlignWindow(this, wx, wy);
566 ma_gui->lock_window("MixersAlign::new_gui");
567 ma_gui->create_objects();
568 ma_gui->load_lists();
569 ma_gui->default_selection();
570 ma_gui->show_window(1);
571 ma_gui->unlock_window();
575 void MixersAlign::apply()
577 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
578 int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
580 for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
581 if( m == mid ) continue; // master does not move
582 MixersAlignMixer *mix = mixers[m];
583 Mixer *mixer = mix->mixer;
584 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
585 int id = mixer->mixer_ids[i];
586 Track *track = mwindow->edl->tracks->first;
587 while( track && track->mixer_id != id ) track = track->next;
588 if( !track ) continue;
589 int64_t dt = track->to_units(mix->nudge, 0);
590 for( Edit *edit=track->edits->first; edit; edit=edit->next )
591 edit->startproject += dt;
596 mwindow->gui->lock_window("MixersAlign::handle_done_event");
597 mwindow->update_gui(1);
598 mwindow->gui->unlock_window();
602 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
605 this->dialog = dialog;
607 MixersAlignThread::~MixersAlignThread()
612 void MixersAlignThread::run()
614 dialog->update_match();
615 MixersAlignWindow *ma_gui = dialog->ma_gui;
616 ma_gui->lock_window("MixersAlignThread::run");
617 ma_gui->reset->enable();
618 ma_gui->match->enable();
619 ma_gui->apply->enable();
620 ma_gui->undo->enable();
621 ma_gui->unlock_window();
625 void MixersAlign::handle_done_event(int result)
628 EDL *edl = mwindow->edl;
629 mwindow->edl = undo_edl;
630 mwindow->undo_before();
632 mwindow->undo_after(_("align mixers"), LOAD_ALL);
634 else if( thread->running() ) {
640 void MixersAlign::handle_close_event(int result)
642 undo_edl->remove_user();
647 void MixersAlign::load_mixers()
649 mixers.remove_all_objects();
650 Mixers &edl_mixers = mwindow->edl->mixers;
651 for( int i=0; i<edl_mixers.size(); ++i )
652 mixers.append(new MixersAlignMixer(edl_mixers[i]));
655 void MixersAlign::load_mtracks()
657 mtracks.remove_all_objects();
658 Track *track=mwindow->edl->tracks->first;
659 for( int no=0; track; ++no, track=track->next ) {
660 if( track->data_type != TRACK_AUDIO ) continue;
661 mtracks.append(new MixersAlignMTrack(track, no));
665 void MixersAlign::load_atracks()
667 atracks.remove_all_objects();
668 Track *track=mwindow->edl->tracks->first;
669 for( int no=0; track; ++no, track=track->next ) {
670 if( track->data_type != TRACK_AUDIO ) continue;
671 if( mixer_of(track) < 0 ) continue;
672 atracks.append(new MixersAlignATrack(track, no));
677 int MixersAlign::mixer_of(Track *track, int &midx)
679 int id = track->mixer_id, k = mixers.size(), idx = -1;
680 while( idx < 0 && --k >= 0 )
681 idx = mixers[k]->mixer->mixer_ids.number_of(id);
686 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
688 EDL *edl = new EDL(mwindow->edl);
689 edl->create_objects();
690 Track *track = mwindow->edl->tracks->first;
691 for( ; track; track=track->next ) {
692 int id = track->mixer_id;
693 if( track->data_type != TRACK_AUDIO ) continue;
694 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
695 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
696 mixer_audio->copy_from(track);
697 mixer_audio->play = 1;
702 EDL *MixersAlign::mixer_master_clip(Track *track)
704 EDL *edl = new EDL(mwindow->edl);
705 edl->create_objects();
706 Track *master_audio = edl->tracks->add_audio_track(0, 0);
707 master_audio->copy_from(track);
708 master_audio->play = 1;
712 int64_t MixersAlign::mixer_tracks_total()
714 int64_t total_len = 0;
715 int64_t sample_rate = mwindow->edl->get_sample_rate();
717 for( ; (m=ma_gui->mixer_list->get_selection_number(0, idx))>=0 ; ++idx ) {
718 Mixer *mixer = mixers[m]->mixer;
719 double render_end = 0;
720 Track *track = mwindow->edl->tracks->first;
721 for( ; track; track=track->next ) {
722 int id = track->mixer_id;
723 if( track->data_type != TRACK_AUDIO ) continue;
724 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
725 double track_end = track->get_length();
726 if( render_end < track_end ) render_end = track_end;
728 if( render_end > audio_end ) render_end = audio_end;
729 double len = render_end - audio_start;
730 if( len > 0 ) total_len += len * sample_rate;
735 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
736 : RenderEngine(0, mwindow->preferences, 0, 0)
738 TransportCommand command;
739 command.command = NORMAL_FWD;
740 command.get_edl()->copy_all(edl);
741 command.change_type = CHANGE_ALL;
742 command.realtime = 0;
743 set_vcache(mwindow->video_cache);
744 set_acache(mwindow->audio_cache);
745 arm_command(&command);
748 MixersAlignARender::~MixersAlignARender()
752 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
754 return arender ? arender->process_buffer(samples, len, pos) : -1;
757 MixersAlignPackage::MixersAlignPackage()
762 MixersAlignPackage::~MixersAlignPackage()
766 MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
767 : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
769 this->dialog = dialog;
770 dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
772 MixersAlignFarm::~MixersAlignFarm()
774 dialog->farming->unlock();
777 LoadPackage* MixersAlignFarm::new_package()
779 return new MixersAlignPackage();
782 void MixersAlignFarm::init_packages()
784 for( int i = 0; i < get_total_packages(); ++i ) {
785 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
786 MixersAlignPackage *package = (MixersAlignPackage *)get_package(i);
787 package->mixer = dialog->mixers[m];
791 LoadClient* MixersAlignFarm::new_client()
793 return new MixersAlignClient(this);
796 MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
801 MixersAlignClient::~MixersAlignClient()
805 void MixersAlignClient::process_package(LoadPackage *pkg)
807 MixersAlignFarm *farm = (MixersAlignFarm *)server;
808 MixersAlign *dialog = farm->dialog;
809 MixersAlignPackage *package = (MixersAlignPackage *)pkg;
810 dialog->process_package(farm, package);
813 static inline void conj_product(int n, double *rp, double *ip,
814 double *arp, double *aip, double *brp, double *bip)
816 for( int i=0; i<n; ++i ) {
817 double ar = arp[i], ai = aip[i];
818 double br = brp[i], bi = -bip[i];
819 rp[i] = ar*br - ai*bi;
820 ip[i] = ar*bi + ai*br;
824 void MixersAlign::process_package(MixersAlignFarm *farm,
825 MixersAlignPackage *package)
827 if( progress->is_cancelled() ) failed = -1;
829 MixersAlignMixer *amix = package->mixer;
830 EDL *edl = mixer_audio_clip(amix->mixer);
832 MixersAlignARender audio(mwindow, edl);
833 int sample_rate = edl->get_sample_rate();
834 int channels = edl->get_audio_channels();
835 int64_t audio_samples = edl->get_audio_samples();
836 int64_t cur_pos = audio_start * sample_rate;
837 int64_t end_pos = audio_end * sample_rate;
838 if( end_pos > audio_samples ) end_pos = audio_samples;
839 int len = master_len, len2 = len/2;
840 double *audio_r = new double[len];
841 double *audio_i = new double[len];
842 Samples *samples[2][MAX_CHANNELS];
843 for( int k=0; k<2; ++k ) {
844 for( int i=0; i<MAX_CHANNELS; ++i )
845 samples[k][i] = i<channels ? new Samples(len2) : 0;
848 int64_t nxt_pos = cur_pos+len2;
849 if( nxt_pos > end_pos ) nxt_pos = end_pos;
850 int len1 = nxt_pos - cur_pos;
851 int ret = audio.render(samples[k], len1, cur_pos);
852 while( !ret && !failed && cur_pos < end_pos ) {
853 update_progress(len1);
854 int64_t pos = cur_pos;
855 cur_pos = nxt_pos; nxt_pos += len2;
856 if( nxt_pos > end_pos ) nxt_pos = end_pos;
857 len1 = nxt_pos - cur_pos;
858 ret = audio.render(samples[1-k], len1, cur_pos);
860 Track *track = edl->tracks->first;
861 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
862 int id = track->mixer_id, atk = atracks.size();
863 while( --atk >= 0 && id != atracks[atk]->track->mixer_id );
864 if( atk < 0 ) continue;
866 double *lp = samples[k][ch]->get_data();
867 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
868 double *np = samples[1-k][ch]->get_data();
869 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
870 while( i < len ) audio_r[i++] = 0;
871 do_fft(len, 0, audio_r, 0, audio_r, audio_i);
872 conj_product(len, audio_r, audio_i,
873 audio_r, audio_i, master_r, master_i);
874 do_fft(len, 1, audio_r, audio_i, audio_r, audio_i);
875 double mx = 0; int64_t mi = -1;
876 for( int i=0; i<len2; ++i ) {
877 double v = fabs(audio_r[i]);
878 if( mx < v ) { mx = v; mi = i + pos; }
881 MixersAlignATrack *atrack= atracks[atk];
882 if( atrack->mx < mx ) {
888 if( progress->is_cancelled() ) failed = -1;
890 if( ret && !failed ) {
891 eprintf("Audio render failed:\n%s", edl->path);
896 for( int k=0; k<2; ++k ) {
897 for( int i=channels; --i>=0; )
898 delete samples[k][i];
904 void MixersAlign::update_progress(int64_t len)
907 total_rendered += len;
908 total_lock->unlock();
909 progress->update(total_rendered);
912 static inline uint64_t high_bit_mask(uint64_t n)
914 n |= n >> 1; n |= n >> 2;
915 n |= n >> 4; n |= n >> 8;
916 n |= n >> 16; n |= n >> 32;
920 void MixersAlign::load_master_audio(Track *track)
922 EDL *edl = mixer_master_clip(track);
923 MixersAlignARender audio(mwindow, edl);
924 int sample_rate = edl->get_sample_rate();
925 int channels = edl->get_audio_channels();
926 int64_t audio_samples = edl->get_audio_samples();
927 int64_t cur_pos = master_start * sample_rate;
928 int64_t end_pos = master_end * sample_rate;
929 if( end_pos > audio_samples ) end_pos = audio_samples;
930 if( cur_pos >= end_pos ) {
931 eprintf(_("master audio track empty"));
935 int64_t audio_len = end_pos - cur_pos;
936 if( audio_len > sample_rate * 60 ) {
937 eprintf(_("master audio track length > 60 seconds"));
941 int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
942 if( master_len != fft_len ) {
943 master_len = fft_len;
944 delete [] master_r; master_r = new double[master_len];
945 delete [] master_i; master_i = new double[master_len];
947 { Samples *samples[MAX_CHANNELS];
948 for( int i=0; i<MAX_CHANNELS; ++i )
949 samples[i] = i<channels ? new Samples(audio_len) : 0;
950 int ret = audio.render(samples, audio_len, cur_pos);
951 if( ret ) failed = 1;
953 double *dp = samples[0]->get_data();
954 int i = 0; double ss = 0;
955 for( ; i<audio_len; ++i ) { ss += *dp * *dp; master_r[i] = *dp++; }
957 for( ; i<fft_len; ++i ) master_r[i] = 0;
958 for( int i=channels; --i>=0; )
961 do_fft(fft_len, 0, master_r, 0, master_r, master_i);
964 void MixersAlign::scan_mixer_audio()
966 for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
968 while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
970 eprintf(_("no mixers selected"));
976 MixersAlignFarm farm(this, n);
977 int64_t total_len = mixer_tracks_total();
978 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
979 progress = mwindow->mainprogress->
980 start_progress(_("Render mixer audio"), total_len);
981 mwindow->gui->unlock_window();
983 farm.process_packages();
984 if( progress->is_cancelled() ) failed = -1;
987 double secs = timer.get_difference()/1000.;
988 sprintf(text, _("Render mixer done: %0.3f secs"), secs);
989 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
991 mwindow->mainprogress->end_progress(progress);
993 mwindow->gui->show_message(text);
994 mwindow->gui->unlock_window();
997 void MixersAlign::update_match()
999 int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1001 eprintf(_("selection (master) not set"));
1004 master_start = mwindow->edl->local_session->get_inpoint();
1005 if( master_start < 0 ) {
1006 eprintf(_("in point selection (master start) must be set"));
1009 master_end = mwindow->edl->local_session->get_outpoint();
1010 if( master_end < 0 ) {
1011 eprintf(_("out point selection (master end) must be set"));
1014 if( master_start >= master_end ) {
1015 eprintf(_("in/out point selection (master start/end) invalid"));
1018 audio_start = mwindow->edl->local_session->get_selectionstart();
1019 audio_end = mwindow->edl->local_session->get_selectionend();
1020 if( audio_start >= audio_end ) {
1021 eprintf(_("selection (audio start/end) invalid"));
1026 if( !failed ) load_master_audio(mtracks[midx]->track);
1027 if( !failed ) scan_mixer_audio();
1028 if( !failed ) update();
1030 mwindow->gui->lock_window("MixersAlign::update_match");
1031 mwindow->gui->show_message(_("mixer selection match canceled"));
1032 mwindow->gui->unlock_window();
1034 else if( failed > 0 )
1035 eprintf(_("Error in match render."));
1038 void MixersAlign::update()
1040 // mixer best matches
1041 for( int m=0; m<mixers.size(); ++m ) {
1042 MixersAlignMixer *mix = mixers[m];
1043 Mixer *mixer = mix->mixer;
1044 mix->mx = 0; mix->mi = -1; mix->aidx = -1;
1045 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1046 int id = mixer->mixer_ids[i], k = atracks.size();
1047 while( --k >= 0 && atracks[k]->track->mixer_id != id );
1048 if( k < 0 ) continue;
1049 MixersAlignATrack *atrk = atracks[k];
1050 atrk->nudge = atrk->mi < 0 ? 0 :
1051 master_start - atrk->track->from_units(atrk->mi);
1052 if( mix->mx >= atrk->mx ) continue;
1056 mix->nudge = atrk->nudge;
1060 ma_gui->lock_window("MixersAlign::update");
1061 ma_gui->mixer_list->load_list();
1062 ma_gui->mixer_list->set_all_selected(1);
1063 ma_gui->mixer_list->update();
1065 ma_gui->atrack_list->load_list();
1066 for( int m=0; m<mixers.size(); ++m ) {
1067 int aidx = mixers[m]->aidx;
1068 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1070 ma_gui->atrack_list->update();
1071 ma_gui->unlock_window();