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 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
54 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
55 N_("Mixer"), N_("Nudge"),
58 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
62 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
63 MixersAlign *dialog, int x, int y, int w, int h)
64 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
66 set_selection_mode(LISTBOX_MULTIPLE);
67 this->dialog = dialog;
69 for( int i=MIX_SZ; --i>=0; ) {
70 col_widths[i] = mix_widths[i];
71 col_titles[i] = _(mix_titles[i]);
75 MixersAlignMixerList::~MixersAlignMixerList()
80 void MixersAlignMixerList::clear()
82 for( int i=MIX_SZ; --i>=0; )
83 cols[i].remove_all_objects();
86 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
88 char mixer_text[BCSTRLEN];
89 snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
90 mixer->mixer->idx, mixer->mixer->title);
91 cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
92 char nudge_text[BCSTRLEN];
93 sprintf(nudge_text, _("%0.4f"), mixer->nudge);
94 cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
97 void MixersAlignMixerList::load_list()
100 for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
101 add_mixer(dialog->mixers[i]);
104 void MixersAlignMixerList::update()
106 int xpos = get_xposition(), ypos = get_yposition();
107 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
110 int MixersAlignMixerList::selection_changed()
112 for( int m=0; m<dialog->mixers.size(); ++m ) {
113 MixersAlignMixer *mix = dialog->mixers[m];
114 if( !is_selected(m) ) {
115 for( int i=0; i<dialog->atracks.size(); ++i ) {
116 if( m != dialog->amixer_of(i) ) continue;
117 gui->atrack_list->set_selected(i, 0);
124 else if( mix->aidx < 0 ) {
125 MixersAlignATrack *best = 0; int idx = -1;
126 for( int i=0; i<dialog->atracks.size(); ++i ) {
127 if( m != dialog->amixer_of(i) ) continue;
128 MixersAlignATrack *atrk = dialog->atracks[i];
129 if( atrk->mi < 0 ) continue;
130 gui->atrack_list->set_selected(i, 0);
131 if( best && best->mx >= atrk->mx ) continue;
132 best = atrk; idx = i;
134 if( idx >= 0 && best ) {
135 gui->atrack_list->set_selected(idx, 1);
136 MixersAlignMixer *mix = dialog->mixers[m];
140 mix->nudge = best->nudge;
143 for( int i=0; i<dialog->atracks.size(); ++i ) {
144 if( m != dialog->amixer_of(i) ) continue;
145 gui->atrack_list->set_selected(i, 1);
150 gui->atrack_list->update();
153 for( int i=0; i<dialog->atracks.size(); ++i ) {
154 if( !gui->atrack_list->is_selected(i) ) continue;
155 int m = dialog->amixer_of(i);
156 if( m >= 0 ) set_selected(m, 1);
162 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
169 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
170 N_("No"), N_("Mixer"), N_("Track"),
173 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
177 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
178 MixersAlign *dialog, int x, int y, int w, int h)
179 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
181 set_selection_mode(LISTBOX_SINGLE);
182 this->dialog = dialog;
184 for( int i=MTK_SZ; --i>=0; ) {
185 col_widths[i] = mtk_widths[i];
186 col_titles[i] = _(mtk_titles[i]);
190 MixersAlignMTrackList::~MixersAlignMTrackList()
195 void MixersAlignMTrackList::clear()
197 for( int i=MTK_SZ; --i>=0; )
198 cols[i].remove_all_objects();
201 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
203 char no_text[BCSTRLEN];
204 snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
205 cols[MTK_NO].append(new BC_ListBoxItem(no_text));
206 char mixer_text[BCSTRLEN];
207 Track *track = mtrk->track;
208 int midx = -1, m = dialog->mixer_of(track, midx);
209 snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,midx);
210 cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
211 cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
214 void MixersAlignMTrackList::load_list()
217 for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
218 add_mtrack(dialog->mtracks[i]);
221 void MixersAlignMTrackList::update()
223 int xpos = get_xposition(), ypos = get_yposition();
224 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
228 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
237 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
238 N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
241 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
245 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
246 MixersAlign *dialog, int x, int y, int w, int h)
247 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
249 set_selection_mode(LISTBOX_MULTIPLE);
250 this->dialog = dialog;
252 for( int i=ATK_SZ; --i>=0; ) {
253 col_widths[i] = atk_widths[i];
254 col_titles[i] = _(atk_titles[i]);
258 MixersAlignATrackList::~MixersAlignATrackList()
263 void MixersAlignATrackList::clear()
265 for( int i=ATK_SZ; --i>=0; )
266 cols[i].remove_all_objects();
269 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
271 char atrack_text[BCSTRLEN];
272 Track *track = atrack->track;
273 int midx = -1, m = dialog->mixer_of(track, midx);
274 snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
275 cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
276 cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
277 char nudge_text[BCSTRLEN];
278 sprintf(nudge_text, "%0.4f", atrack->nudge);
279 cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
280 char mx_text[BCSTRLEN];
281 sprintf(mx_text, "%0.4f", atrack->mx);
282 cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
283 char mi_text[BCSTRLEN];
284 sprintf(mi_text, "%jd", atrack->mi);
285 cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
289 void MixersAlignATrackList::load_list()
292 for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
293 add_atrack(dialog->atracks[i]);
296 void MixersAlignATrackList::update()
298 int xpos = get_xposition(), ypos = get_yposition();
299 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
302 int MixersAlignATrackList::selection_changed()
304 int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
305 int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
308 MixersAlignMixer *mix = dialog->mixers[m];
309 for( int i=0; i<dialog->atracks.size(); ++i ) {
310 int k = dialog->amixer_of(i);
311 if( k < 0 ) { set_selected(i, 0); continue; }
312 if( m != k ) continue;
313 MixersAlignATrack *atrk = dialog->atracks[i];
314 if( atrk->mi < 0 ) continue;
315 int is_sel = is_selected(i);
317 if( i != idx ) continue;
322 mix->nudge = atrk->nudge;
333 set_selected(idx, 1);
337 gui->mixer_list->load_list();
338 for( int i=0; i<dialog->atracks.size(); ++i ) {
339 if( !is_selected(i) ) continue;
340 int m = dialog->amixer_of(i);
341 if( m < 0 ) continue;
342 gui->mixer_list->set_selected(m, 1);
344 gui->mixer_list->update();
348 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
349 MixersAlign *dialog, int x, int y)
350 : BC_GenericButton(x, y, _("Reset"))
353 this->dialog = dialog;
356 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
358 return BC_GenericButton::calculate_w(gui, _("Reset"));
361 int MixersAlignReset::handle_event()
363 dialog->load_mixers();
364 dialog->load_mtracks();
365 dialog->load_atracks();
367 gui->default_selection();
371 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
372 MixersAlign *dialog, int x, int y)
373 : BC_GenericButton(x, y, _("Match"))
376 this->dialog = dialog;
379 int MixersAlignMatch::handle_event()
381 if( !dialog->thread->running() ) {
382 gui->reset->disable();
383 gui->match->disable();
384 gui->apply->disable();
385 gui->undo->disable();
386 dialog->thread->start();
391 MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
392 MixersAlign *dialog, int x, int y)
393 : BC_GenericButton(x, y, _("Apply"))
396 this->dialog = dialog;
399 int MixersAlignApply::calculate_width(BC_WindowBase *gui)
401 return BC_GenericButton::calculate_w(gui, _("Apply"));
404 int MixersAlignApply::handle_event()
410 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
411 MixersAlign *dialog, int x, int y)
412 : BC_GenericButton(x, y, _("Undo"))
415 this->dialog = dialog;
418 int MixersAlignUndo::handle_event()
420 MWindow *mwindow = dialog->mwindow;
421 mwindow->edl->copy_all(dialog->undo_edl);
422 mwindow->gui->lock_window("MixersAlignUndo::handle_event");
423 mwindow->update_gui(1);
424 mwindow->gui->unlock_window();
425 return gui->reset->handle_event();
429 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
430 : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
432 this->dialog = dialog;
434 MixersAlignWindow::~MixersAlignWindow()
438 void MixersAlignWindow::create_objects()
440 int x = 10, y = 10, w4 = (get_w()-x-10)/4, lw = w4 + 20;
441 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
442 mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
443 add_subwindow(mixer_title);
444 mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
445 add_subwindow(mtrack_title);
446 atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
447 add_subwindow(atrack_title);
448 y += mixer_title->get_h() + 10;
449 int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 15;
451 mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
452 add_subwindow(mixer_list);
453 mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-20, lh);
454 add_subwindow(mtrack_list);
455 atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
456 add_subwindow(atrack_list);
457 int xr = x2-10 - MixersAlignReset::calculate_width(this);
458 add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y2+20));
459 add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y2+20));
460 int xa = x3-10 - MixersAlignApply::calculate_width(this);
461 add_subwindow(apply = new MixersAlignApply(this, dialog, xa, y2+20));
462 add_subwindow(undo = new MixersAlignUndo(this, dialog, x3+10, y2+20));
464 add_subwindow(new BC_OKButton(this));
465 add_subwindow(new BC_CancelButton(this));
468 int MixersAlignWindow::resize_event(int w, int h)
470 int x = 10, y = 10, w4 = (w-x-10)/4, lw = w4 + 20;
471 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
472 mixer_title->reposition_window(x1, y);
473 mtrack_title->reposition_window(x2, y);
474 atrack_title->reposition_window(x3, y);
475 y += mixer_title->get_h() + 10;
476 int y1 = y, y2 = h - BC_OKButton::calculate_h() - 15;
478 mixer_list->reposition_window(x1, y, x2-x1-20, lh);
479 mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
480 atrack_list->reposition_window(x3, y, x4-x3-20, lh);
481 int xr = x2-10 - MixersAlignReset::calculate_width(this);
482 reset->reposition_window(xr, y2+20);
483 match->reposition_window(x2+10, y2+20);
484 int xa = x3-10 - MixersAlignApply::calculate_width(this);
485 apply->reposition_window(xa, y2+20);
486 undo->reposition_window(x3+10, y2+20);
490 void MixersAlignWindow::load_lists()
492 mixer_list->load_list();
493 mtrack_list->load_list();
494 atrack_list->load_list();
497 void MixersAlignWindow::default_selection()
499 // mixers selects all mixers
500 mixer_list->set_all_selected(1);
501 // master selects first mixer audio track
502 for( int i=0; i<dialog->mtracks.size(); ++i ) {
503 if( dialog->mmixer_of(i) >= 0 ) {
504 mtrack_list->set_selected(i, 1);
508 // audio selects all mixer audio tracks
509 for( int i=0; i<dialog->atracks.size(); ++i ) {
510 if( dialog->amixer_of(i) >= 0 )
511 atrack_list->set_selected(i, 1);
516 void MixersAlignWindow::update_gui()
518 mixer_list->update();
519 mtrack_list->update();
520 atrack_list->update();
524 MixersAlign::MixersAlign(MWindow *mwindow)
526 this->mwindow = mwindow;
528 farming = new Mutex("MixersAlign::farming");
529 total_lock = new Mutex("MixersAlign::total_lock");
530 thread = new MixersAlignThread(this);
538 master_start = master_end = 0;
539 audio_start = audio_end = 0;
542 MixersAlign::~MixersAlign()
545 farming->lock("MixersAlign::~MixersAlign");
555 void MixersAlign::start_dialog(int wx, int wy)
559 this->undo_edl = new EDL();
560 undo_edl->create_objects();
561 undo_edl->copy_all(mwindow->edl);
565 BC_Window *MixersAlign::new_gui()
570 ma_gui = new MixersAlignWindow(this, wx, wy);
571 ma_gui->lock_window("MixersAlign::new_gui");
572 ma_gui->create_objects();
573 ma_gui->load_lists();
574 ma_gui->default_selection();
575 ma_gui->show_window(1);
576 ma_gui->unlock_window();
580 void MixersAlign::apply()
582 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
583 int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
584 EDL *edl = mwindow->edl;
586 for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
587 if( m == mid ) continue; // master does not move
588 MixersAlignMixer *mix = mixers[m];
589 Mixer *mixer = mix->mixer;
590 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
591 int id = mixer->mixer_ids[i];
592 Track *track = edl->tracks->first;
593 while( track && track->mixer_id != id ) track = track->next;
594 if( !track ) continue;
595 double nudge = mix->nudge;
596 int record = track->record; track->record = 1;
598 edl->clear(0, -nudge,
599 edl->session->labels_follow_edits,
600 edl->session->plugins_follow_edits,
601 edl->session->autos_follow_edits);
603 else if( nudge > 0 ) {
604 edl->paste_silence(0, nudge,
605 edl->session->labels_follow_edits,
606 edl->session->plugins_follow_edits,
607 edl->session->autos_follow_edits);
609 track->record = record;
614 mwindow->gui->lock_window("MixersAlign::handle_done_event");
615 mwindow->update_gui(1);
616 mwindow->gui->unlock_window();
620 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
623 this->dialog = dialog;
625 MixersAlignThread::~MixersAlignThread()
630 void MixersAlignThread::run()
632 dialog->update_match();
633 MixersAlignWindow *ma_gui = dialog->ma_gui;
634 ma_gui->lock_window("MixersAlignThread::run");
635 ma_gui->reset->enable();
636 ma_gui->match->enable();
637 ma_gui->apply->enable();
638 ma_gui->undo->enable();
639 ma_gui->unlock_window();
643 void MixersAlign::handle_done_event(int result)
645 if( thread->running() ) {
651 EDL *edl = mwindow->edl;
652 mwindow->edl = undo_edl;
653 mwindow->undo_before();
655 mwindow->undo_after(_("align mixers"), LOAD_ALL);
659 void MixersAlign::handle_close_event(int result)
661 undo_edl->remove_user();
666 void MixersAlign::load_mixers()
668 mixers.remove_all_objects();
669 Mixers &edl_mixers = mwindow->edl->mixers;
670 for( int i=0; i<edl_mixers.size(); ++i )
671 mixers.append(new MixersAlignMixer(edl_mixers[i]));
674 void MixersAlign::load_mtracks()
676 mtracks.remove_all_objects();
677 Track *track=mwindow->edl->tracks->first;
678 for( int no=0; track; ++no, track=track->next ) {
679 if( track->data_type != TRACK_AUDIO ) continue;
680 mtracks.append(new MixersAlignMTrack(track, no));
684 void MixersAlign::load_atracks()
686 atracks.remove_all_objects();
687 Track *track=mwindow->edl->tracks->first;
688 for( int no=0; track; ++no, track=track->next ) {
689 if( track->data_type != TRACK_AUDIO ) continue;
690 if( mixer_of(track) < 0 ) continue;
691 atracks.append(new MixersAlignATrack(track, no));
696 int MixersAlign::mixer_of(Track *track, int &midx)
698 int id = track->mixer_id, k = mixers.size(), idx = -1;
699 while( idx < 0 && --k >= 0 )
700 idx = mixers[k]->mixer->mixer_ids.number_of(id);
705 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
707 EDL *edl = new EDL(mwindow->edl);
708 edl->create_objects();
709 Track *track = mwindow->edl->tracks->first;
710 for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
711 int id = track->mixer_id;
712 if( track->data_type != TRACK_AUDIO ) continue;
713 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
714 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
715 mixer_audio->copy_settings(track);
716 mixer_audio->play = 1;
717 mixer_audio->edits->copy_from(track->edits);
718 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
719 autos[AUTOMATION_PAN]->default_auto;
720 pan_auto->values[ch++] = 1.0;
725 EDL *MixersAlign::mixer_master_clip(Track *track)
727 EDL *edl = new EDL(mwindow->edl);
728 edl->create_objects();
729 Track *master_audio = edl->tracks->add_audio_track(0, 0);
730 master_audio->copy_settings(track);
731 master_audio->play = 1;
732 master_audio->edits->copy_from(track->edits);
733 PanAuto* pan_auto = (PanAuto*)master_audio->automation->
734 autos[AUTOMATION_PAN]->default_auto;
735 pan_auto->values[0] = 1.0;
739 int64_t MixersAlign::mixer_tracks_total()
741 int64_t total_len = 0;
742 int64_t sample_rate = mwindow->edl->get_sample_rate();
744 for( ; (m=ma_gui->mixer_list->get_selection_number(0, idx))>=0 ; ++idx ) {
745 Mixer *mixer = mixers[m]->mixer;
746 double render_end = 0;
747 Track *track = mwindow->edl->tracks->first;
748 for( ; track; track=track->next ) {
749 int id = track->mixer_id;
750 if( track->data_type != TRACK_AUDIO ) continue;
751 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
752 double track_end = track->get_length();
753 if( render_end < track_end ) render_end = track_end;
755 if( render_end > audio_end ) render_end = audio_end;
756 double len = render_end - audio_start;
757 if( len > 0 ) total_len += len * sample_rate;
762 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
763 : RenderEngine(0, mwindow->preferences, 0, 0)
765 TransportCommand command;
766 command.command = NORMAL_FWD;
767 command.get_edl()->copy_all(edl);
768 command.change_type = CHANGE_ALL;
769 command.realtime = 0;
770 set_vcache(mwindow->video_cache);
771 set_acache(mwindow->audio_cache);
772 arm_command(&command);
775 MixersAlignARender::~MixersAlignARender()
779 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
781 return arender ? arender->process_buffer(samples, len, pos) : -1;
784 MixersAlignPackage::MixersAlignPackage()
789 MixersAlignPackage::~MixersAlignPackage()
793 MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
794 : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
796 this->dialog = dialog;
797 dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
799 MixersAlignFarm::~MixersAlignFarm()
801 dialog->farming->unlock();
804 LoadPackage* MixersAlignFarm::new_package()
806 return new MixersAlignPackage();
809 void MixersAlignFarm::init_packages()
811 for( int i = 0; i < get_total_packages(); ++i ) {
812 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
813 MixersAlignPackage *package = (MixersAlignPackage *)get_package(i);
814 package->mixer = dialog->mixers[m];
818 LoadClient* MixersAlignFarm::new_client()
820 return new MixersAlignClient(this);
823 MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
828 MixersAlignClient::~MixersAlignClient()
832 void MixersAlignClient::process_package(LoadPackage *pkg)
834 MixersAlignFarm *farm = (MixersAlignFarm *)server;
835 MixersAlign *dialog = farm->dialog;
836 MixersAlignPackage *package = (MixersAlignPackage *)pkg;
837 dialog->process_package(farm, package);
840 static inline void conj_product(int n, double *rp, double *ip,
841 double *arp, double *aip, double *brp, double *bip)
843 for( int i=0; i<n; ++i ) {
844 double ar = arp[i], ai = aip[i];
845 double br = brp[i], bi = -bip[i];
846 rp[i] = ar*br - ai*bi;
847 ip[i] = ar*bi + ai*br;
851 void MixersAlign::process_package(MixersAlignFarm *farm,
852 MixersAlignPackage *package)
854 if( progress->is_cancelled() ) failed = -1;
856 MixersAlignMixer *amix = package->mixer;
857 EDL *edl = mixer_audio_clip(amix->mixer);
859 MixersAlignARender audio(mwindow, edl);
860 int sample_rate = edl->get_sample_rate();
861 int channels = edl->get_audio_channels();
862 int64_t audio_samples = edl->get_audio_samples();
863 int64_t cur_pos = audio_start * sample_rate;
864 int64_t end_pos = audio_end * sample_rate;
865 if( end_pos > audio_samples ) end_pos = audio_samples;
866 int len = master_len, len2 = len/2;
867 double *audio_r = new double[len];
868 double *audio_i = new double[len];
869 Samples *samples[2][MAX_CHANNELS];
870 for( int k=0; k<2; ++k ) {
871 for( int i=0; i<MAX_CHANNELS; ++i )
872 samples[k][i] = i<channels ? new Samples(len2) : 0;
875 int64_t nxt_pos = cur_pos+len2;
876 if( nxt_pos > end_pos ) nxt_pos = end_pos;
877 int len1 = nxt_pos - cur_pos;
878 int ret = audio.render(samples[k], len1, cur_pos);
879 while( !ret && !failed && cur_pos < end_pos ) {
880 update_progress(len1);
881 int64_t pos = cur_pos;
882 cur_pos = nxt_pos; nxt_pos += len2;
883 if( nxt_pos > end_pos ) nxt_pos = end_pos;
884 len1 = nxt_pos - cur_pos;
885 ret = audio.render(samples[1-k], len1, cur_pos);
887 Track *track = edl->tracks->first;
888 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
889 int id = track->mixer_id, atk = atracks.size();
890 while( --atk >= 0 && id != atracks[atk]->track->mixer_id );
891 if( atk < 0 ) continue;
893 double *lp = samples[k][ch]->get_data();
894 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
895 double *np = samples[1-k][ch]->get_data();
896 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
897 while( i < len ) audio_r[i++] = 0;
898 do_fft(len, 0, audio_r, 0, audio_r, audio_i);
899 conj_product(len, audio_r, audio_i,
900 audio_r, audio_i, master_r, master_i);
901 do_fft(len, 1, audio_r, audio_i, audio_r, audio_i);
902 double mx = 0; int64_t mi = -1;
903 for( int i=0; i<len2; ++i ) {
904 double v = fabs(audio_r[i]);
905 if( mx < v ) { mx = v; mi = i + pos; }
908 MixersAlignATrack *atrack= atracks[atk];
909 if( atrack->mx < mx ) {
915 if( progress->is_cancelled() ) failed = -1;
917 if( ret && !failed ) {
918 eprintf("Audio render failed:\n%s", edl->path);
923 for( int k=0; k<2; ++k ) {
924 for( int i=channels; --i>=0; )
925 delete samples[k][i];
931 void MixersAlign::update_progress(int64_t len)
934 total_rendered += len;
935 total_lock->unlock();
936 progress->update(total_rendered);
939 static inline uint64_t high_bit_mask(uint64_t n)
941 n |= n >> 1; n |= n >> 2;
942 n |= n >> 4; n |= n >> 8;
943 n |= n >> 16; n |= n >> 32;
947 void MixersAlign::load_master_audio(Track *track)
949 EDL *edl = mixer_master_clip(track);
950 MixersAlignARender audio(mwindow, edl);
951 int sample_rate = edl->get_sample_rate();
952 int channels = edl->get_audio_channels();
953 int64_t audio_samples = edl->get_audio_samples();
954 int64_t cur_pos = master_start * sample_rate;
955 int64_t end_pos = master_end * sample_rate;
956 if( end_pos > audio_samples ) end_pos = audio_samples;
957 if( cur_pos >= end_pos ) {
958 eprintf(_("master audio track empty"));
962 int64_t audio_len = end_pos - cur_pos;
963 if( audio_len > sample_rate * 60 ) {
964 eprintf(_("master audio track length > 60 seconds"));
968 int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
969 if( master_len != fft_len ) {
970 master_len = fft_len;
971 delete [] master_r; master_r = new double[master_len];
972 delete [] master_i; master_i = new double[master_len];
974 { Samples *samples[MAX_CHANNELS];
975 for( int i=0; i<MAX_CHANNELS; ++i )
976 samples[i] = i<channels ? new Samples(audio_len) : 0;
977 int ret = audio.render(samples, audio_len, cur_pos);
978 if( ret ) failed = 1;
980 double *dp = samples[0]->get_data();
981 int i = 0; double ss = 0;
982 for( ; i<audio_len; ++i ) { ss += *dp * *dp; master_r[i] = *dp++; }
984 for( ; i<fft_len; ++i ) master_r[i] = 0;
985 for( int i=channels; --i>=0; )
988 do_fft(fft_len, 0, master_r, 0, master_r, master_i);
991 void MixersAlign::scan_mixer_audio()
993 for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
995 while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
997 eprintf(_("no mixers selected"));
1003 MixersAlignFarm farm(this, n);
1004 int64_t total_len = mixer_tracks_total();
1005 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
1006 progress = mwindow->mainprogress->
1007 start_progress(_("Render mixer audio"), total_len);
1008 mwindow->gui->unlock_window();
1010 farm.process_packages();
1011 if( progress->is_cancelled() ) failed = -1;
1013 char text[BCSTRLEN];
1014 double secs = timer.get_difference()/1000.;
1015 sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1016 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
1017 progress->update(0);
1018 mwindow->mainprogress->end_progress(progress);
1020 mwindow->gui->show_message(text);
1021 mwindow->gui->unlock_window();
1024 void MixersAlign::update_match()
1026 int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1028 eprintf(_("selection (master) not set"));
1031 master_start = mwindow->edl->local_session->get_inpoint();
1032 if( master_start < 0 ) {
1033 eprintf(_("in point selection (master start) must be set"));
1036 master_end = mwindow->edl->local_session->get_outpoint();
1037 if( master_end < 0 ) {
1038 eprintf(_("out point selection (master end) must be set"));
1041 if( master_start >= master_end ) {
1042 eprintf(_("in/out point selection (master start/end) invalid"));
1045 audio_start = mwindow->edl->local_session->get_selectionstart();
1046 audio_end = mwindow->edl->local_session->get_selectionend();
1047 if( audio_start >= audio_end ) {
1048 eprintf(_("selection (audio start/end) invalid"));
1053 if( !failed ) load_master_audio(mtracks[midx]->track);
1054 if( !failed ) scan_mixer_audio();
1055 if( !failed ) update();
1057 mwindow->gui->lock_window("MixersAlign::update_match");
1058 mwindow->gui->show_message(_("mixer selection match canceled"));
1059 mwindow->gui->unlock_window();
1061 else if( failed > 0 )
1062 eprintf(_("Error in match render."));
1065 void MixersAlign::update()
1067 // mixer best matches
1068 for( int m=0; m<mixers.size(); ++m ) {
1069 MixersAlignMixer *mix = mixers[m];
1070 Mixer *mixer = mix->mixer;
1071 mix->mx = 0; mix->mi = -1; mix->aidx = -1;
1072 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1073 int id = mixer->mixer_ids[i], k = atracks.size();
1074 while( --k >= 0 && atracks[k]->track->mixer_id != id );
1075 if( k < 0 ) continue;
1076 MixersAlignATrack *atrk = atracks[k];
1077 atrk->nudge = atrk->mi < 0 ? 0 :
1078 master_start - atrk->track->from_units(atrk->mi);
1079 if( mix->mx >= atrk->mx ) continue;
1083 mix->nudge = atrk->nudge;
1087 ma_gui->lock_window("MixersAlign::update");
1088 ma_gui->mixer_list->load_list();
1089 ma_gui->mixer_list->set_all_selected(1);
1090 ma_gui->mixer_list->update();
1092 ma_gui->atrack_list->load_list();
1093 for( int m=0; m<mixers.size(); ++m ) {
1094 int aidx = mixers[m]->aidx;
1095 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1097 ma_gui->atrack_list->update();
1098 ma_gui->unlock_window();