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 "localsession.h"
30 #include "mainerror.h"
31 #include "mainprogress.h"
33 #include "mwindowgui.h"
36 #include "preferences.h"
39 #include "renderengine.h"
42 #include "transportque.h"
45 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
53 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
54 N_("Mixer"), N_("Nudge"),
57 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
61 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
62 MixersAlign *dialog, int x, int y, int w, int h)
63 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
65 set_selection_mode(LISTBOX_MULTIPLE);
66 this->dialog = dialog;
68 for( int i=MIX_SZ; --i>=0; ) {
69 col_widths[i] = mix_widths[i];
70 col_titles[i] = _(mix_titles[i]);
74 MixersAlignMixerList::~MixersAlignMixerList()
79 void MixersAlignMixerList::clear()
81 for( int i=MIX_SZ; --i>=0; )
82 cols[i].remove_all_objects();
85 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
87 char mixer_text[BCSTRLEN];
88 snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
89 mixer->mixer->idx, mixer->mixer->title);
90 cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
91 char nudge_text[BCSTRLEN];
92 sprintf(nudge_text, _("%0.4f"), mixer->nudge);
93 cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
96 void MixersAlignMixerList::load_list()
99 for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
100 add_mixer(dialog->mixers[i]);
103 void MixersAlignMixerList::update()
105 int xpos = get_xposition(), ypos = get_yposition();
106 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
109 int MixersAlignMixerList::selection_changed()
111 for( int m=0; m<dialog->mixers.size(); ++m ) {
112 MixersAlignMixer *mix = dialog->mixers[m];
113 if( !is_selected(m) ) {
114 for( int i=0; i<dialog->atracks.size(); ++i ) {
115 if( m != dialog->amixer_of(i) ) continue;
116 gui->atrack_list->set_selected(i, 0);
123 else if( mix->aidx < 0 ) {
124 MixersAlignATrack *best = 0; int idx = -1;
125 for( int i=0; i<dialog->atracks.size(); ++i ) {
126 if( m != dialog->amixer_of(i) ) continue;
127 MixersAlignATrack *atrk = dialog->atracks[i];
128 if( atrk->mi < 0 ) continue;
129 gui->atrack_list->set_selected(i, 0);
130 if( best && best->mx >= atrk->mx ) continue;
131 best = atrk; idx = i;
133 if( idx >= 0 && best ) {
134 gui->atrack_list->set_selected(idx, 1);
135 MixersAlignMixer *mix = dialog->mixers[m];
139 mix->nudge = best->nudge;
142 for( int i=0; i<dialog->atracks.size(); ++i ) {
143 if( m != dialog->amixer_of(i) ) continue;
144 gui->atrack_list->set_selected(i, 1);
149 gui->atrack_list->update();
152 for( int i=0; i<dialog->atracks.size(); ++i ) {
153 if( !gui->atrack_list->is_selected(i) ) continue;
154 int m = dialog->amixer_of(i);
155 if( m >= 0 ) set_selected(m, 1);
161 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
168 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
169 N_("No"), N_("Mixer"), N_("Track"),
172 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
176 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
177 MixersAlign *dialog, int x, int y, int w, int h)
178 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
180 set_selection_mode(LISTBOX_SINGLE);
181 this->dialog = dialog;
183 for( int i=MTK_SZ; --i>=0; ) {
184 col_widths[i] = mtk_widths[i];
185 col_titles[i] = _(mtk_titles[i]);
189 MixersAlignMTrackList::~MixersAlignMTrackList()
194 void MixersAlignMTrackList::clear()
196 for( int i=MTK_SZ; --i>=0; )
197 cols[i].remove_all_objects();
200 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
202 char no_text[BCSTRLEN];
203 snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
204 cols[MTK_NO].append(new BC_ListBoxItem(no_text));
205 char mixer_text[BCSTRLEN];
206 Track *track = mtrk->track;
207 int midx = -1, m = dialog->mixer_of(track, midx);
208 snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,midx);
209 cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
210 cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
213 void MixersAlignMTrackList::load_list()
216 for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
217 add_mtrack(dialog->mtracks[i]);
220 void MixersAlignMTrackList::update()
222 int xpos = get_xposition(), ypos = get_yposition();
223 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
227 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
236 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
237 N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
240 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
244 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
245 MixersAlign *dialog, int x, int y, int w, int h)
246 : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
248 set_selection_mode(LISTBOX_MULTIPLE);
249 this->dialog = dialog;
251 for( int i=ATK_SZ; --i>=0; ) {
252 col_widths[i] = atk_widths[i];
253 col_titles[i] = _(atk_titles[i]);
257 MixersAlignATrackList::~MixersAlignATrackList()
262 void MixersAlignATrackList::clear()
264 for( int i=ATK_SZ; --i>=0; )
265 cols[i].remove_all_objects();
268 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
270 char atrack_text[BCSTRLEN];
271 Track *track = atrack->track;
272 int midx = -1, m = dialog->mixer_of(track, midx);
273 snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
274 cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
275 cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
276 char nudge_text[BCSTRLEN];
277 sprintf(nudge_text, "%0.4f", atrack->nudge);
278 cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
279 char mx_text[BCSTRLEN];
280 sprintf(mx_text, "%0.4f", atrack->mx);
281 cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
282 char mi_text[BCSTRLEN];
283 sprintf(mi_text, "%jd", atrack->mi);
284 cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
288 void MixersAlignATrackList::load_list()
291 for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
292 add_atrack(dialog->atracks[i]);
295 void MixersAlignATrackList::update()
297 int xpos = get_xposition(), ypos = get_yposition();
298 BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
301 int MixersAlignATrackList::selection_changed()
303 int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
304 int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
307 MixersAlignMixer *mix = dialog->mixers[m];
308 for( int i=0; i<dialog->atracks.size(); ++i ) {
309 int k = dialog->amixer_of(i);
310 if( k < 0 ) { set_selected(i, 0); continue; }
311 if( m != k ) continue;
312 MixersAlignATrack *atrk = dialog->atracks[i];
313 if( atrk->mi < 0 ) continue;
314 int is_sel = is_selected(i);
316 if( i != idx ) continue;
321 mix->nudge = atrk->nudge;
332 set_selected(idx, 1);
336 gui->mixer_list->load_list();
337 for( int i=0; i<dialog->atracks.size(); ++i ) {
338 if( !is_selected(i) ) continue;
339 int m = dialog->amixer_of(i);
340 if( m < 0 ) continue;
341 gui->mixer_list->set_selected(m, 1);
343 gui->mixer_list->update();
347 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
348 MixersAlign *dialog, int x, int y)
349 : BC_GenericButton(x, y, _("Reset"))
352 this->dialog = dialog;
355 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
357 return BC_GenericButton::calculate_w(gui, _("Reset"));
360 int MixersAlignReset::handle_event()
362 dialog->load_mixers();
363 dialog->load_mtracks();
364 dialog->load_atracks();
366 gui->default_selection();
370 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
371 MixersAlign *dialog, int x, int y)
372 : BC_GenericButton(x, y, _("Match"))
375 this->dialog = dialog;
378 int MixersAlignMatch::handle_event()
380 if( !dialog->thread->running() ) {
381 gui->reset->disable();
382 gui->match->disable();
383 gui->apply->disable();
384 gui->undo->disable();
385 dialog->thread->start();
390 MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
391 MixersAlign *dialog, int x, int y)
392 : BC_GenericButton(x, y, _("Apply"))
395 this->dialog = dialog;
398 int MixersAlignApply::calculate_width(BC_WindowBase *gui)
400 return BC_GenericButton::calculate_w(gui, _("Apply"));
403 int MixersAlignApply::handle_event()
409 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
410 MixersAlign *dialog, int x, int y)
411 : BC_GenericButton(x, y, _("Undo"))
414 this->dialog = dialog;
417 int MixersAlignUndo::handle_event()
419 MWindow *mwindow = dialog->mwindow;
420 mwindow->edl->copy_all(dialog->undo_edl);
421 mwindow->gui->lock_window("MixersAlignUndo::handle_event");
422 mwindow->update_gui(1);
423 mwindow->gui->unlock_window();
424 return gui->reset->handle_event();
428 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
429 : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
431 this->dialog = dialog;
433 MixersAlignWindow::~MixersAlignWindow()
437 void MixersAlignWindow::create_objects()
439 int x = 10, y = 10, w4 = (get_w()-x-10)/4, lw = w4 + 20;
440 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
441 mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
442 add_subwindow(mixer_title);
443 mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
444 add_subwindow(mtrack_title);
445 atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
446 add_subwindow(atrack_title);
447 y += mixer_title->get_h() + 10;
448 int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 15;
450 mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
451 add_subwindow(mixer_list);
452 mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-20, lh);
453 add_subwindow(mtrack_list);
454 atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
455 add_subwindow(atrack_list);
456 int xr = x2-10 - MixersAlignReset::calculate_width(this);
457 add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y2+20));
458 add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y2+20));
459 int xa = x3-10 - MixersAlignApply::calculate_width(this);
460 add_subwindow(apply = new MixersAlignApply(this, dialog, xa, y2+20));
461 add_subwindow(undo = new MixersAlignUndo(this, dialog, x3+10, y2+20));
463 add_subwindow(new BC_OKButton(this));
464 add_subwindow(new BC_CancelButton(this));
467 int MixersAlignWindow::resize_event(int w, int h)
469 int x = 10, y = 10, w4 = (w-x-10)/4, lw = w4 + 20;
470 int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
471 mixer_title->reposition_window(x1, y);
472 mtrack_title->reposition_window(x2, y);
473 atrack_title->reposition_window(x3, y);
474 y += mixer_title->get_h() + 10;
475 int y1 = y, y2 = h - BC_OKButton::calculate_h() - 15;
477 mixer_list->reposition_window(x1, y, x2-x1-20, lh);
478 mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
479 atrack_list->reposition_window(x3, y, x4-x3-20, lh);
480 int xr = x2-10 - MixersAlignReset::calculate_width(this);
481 reset->reposition_window(xr, y2+20);
482 match->reposition_window(x2+10, y2+20);
483 int xa = x3-10 - MixersAlignApply::calculate_width(this);
484 apply->reposition_window(xa, y2+20);
485 undo->reposition_window(x3+10, y2+20);
489 void MixersAlignWindow::load_lists()
491 mixer_list->load_list();
492 mtrack_list->load_list();
493 atrack_list->load_list();
496 void MixersAlignWindow::default_selection()
498 // mixers selects all mixers
499 mixer_list->set_all_selected(1);
500 // master selects first mixer audio track
501 for( int i=0; i<dialog->mtracks.size(); ++i ) {
502 if( dialog->mmixer_of(i) >= 0 ) {
503 mtrack_list->set_selected(i, 1);
507 // audio selects all mixer audio tracks
508 for( int i=0; i<dialog->atracks.size(); ++i ) {
509 if( dialog->amixer_of(i) >= 0 )
510 atrack_list->set_selected(i, 1);
515 void MixersAlignWindow::update_gui()
517 mixer_list->update();
518 mtrack_list->update();
519 atrack_list->update();
523 MixersAlign::MixersAlign(MWindow *mwindow)
525 this->mwindow = mwindow;
527 farming = new Mutex("MixersAlign::farming");
528 total_lock = new Mutex("MixersAlign::total_lock");
529 thread = new MixersAlignThread(this);
537 master_start = master_end = 0;
538 audio_start = audio_end = 0;
541 MixersAlign::~MixersAlign()
544 farming->lock("MixersAlign::~MixersAlign");
554 void MixersAlign::start_dialog(int wx, int wy)
558 this->undo_edl = new EDL();
559 undo_edl->create_objects();
560 undo_edl->copy_all(mwindow->edl);
564 BC_Window *MixersAlign::new_gui()
569 ma_gui = new MixersAlignWindow(this, wx, wy);
570 ma_gui->lock_window("MixersAlign::new_gui");
571 ma_gui->create_objects();
572 ma_gui->load_lists();
573 ma_gui->default_selection();
574 ma_gui->show_window(1);
575 ma_gui->unlock_window();
579 void MixersAlign::apply()
581 int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
582 int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
584 for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
585 if( m == mid ) continue; // master does not move
586 MixersAlignMixer *mix = mixers[m];
587 Mixer *mixer = mix->mixer;
588 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
589 int id = mixer->mixer_ids[i];
590 Track *track = mwindow->edl->tracks->first;
591 while( track && track->mixer_id != id ) track = track->next;
592 if( !track ) continue;
593 int64_t dt = track->to_units(mix->nudge, 0);
594 for( Edit *edit=track->edits->first; edit; edit=edit->next )
595 edit->startproject += dt;
600 mwindow->gui->lock_window("MixersAlign::handle_done_event");
601 mwindow->update_gui(1);
602 mwindow->gui->unlock_window();
606 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
609 this->dialog = dialog;
611 MixersAlignThread::~MixersAlignThread()
616 void MixersAlignThread::run()
618 dialog->update_match();
619 MixersAlignWindow *ma_gui = dialog->ma_gui;
620 ma_gui->lock_window("MixersAlignThread::run");
621 ma_gui->reset->enable();
622 ma_gui->match->enable();
623 ma_gui->apply->enable();
624 ma_gui->undo->enable();
625 ma_gui->unlock_window();
629 void MixersAlign::handle_done_event(int result)
632 EDL *edl = mwindow->edl;
633 mwindow->edl = undo_edl;
634 mwindow->undo_before();
636 mwindow->undo_after(_("align mixers"), LOAD_ALL);
638 else if( thread->running() ) {
644 void MixersAlign::handle_close_event(int result)
646 undo_edl->remove_user();
651 void MixersAlign::load_mixers()
653 mixers.remove_all_objects();
654 Mixers &edl_mixers = mwindow->edl->mixers;
655 for( int i=0; i<edl_mixers.size(); ++i )
656 mixers.append(new MixersAlignMixer(edl_mixers[i]));
659 void MixersAlign::load_mtracks()
661 mtracks.remove_all_objects();
662 Track *track=mwindow->edl->tracks->first;
663 for( int no=0; track; ++no, track=track->next ) {
664 if( track->data_type != TRACK_AUDIO ) continue;
665 mtracks.append(new MixersAlignMTrack(track, no));
669 void MixersAlign::load_atracks()
671 atracks.remove_all_objects();
672 Track *track=mwindow->edl->tracks->first;
673 for( int no=0; track; ++no, track=track->next ) {
674 if( track->data_type != TRACK_AUDIO ) continue;
675 if( mixer_of(track) < 0 ) continue;
676 atracks.append(new MixersAlignATrack(track, no));
681 int MixersAlign::mixer_of(Track *track, int &midx)
683 int id = track->mixer_id, k = mixers.size(), idx = -1;
684 while( idx < 0 && --k >= 0 )
685 idx = mixers[k]->mixer->mixer_ids.number_of(id);
690 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
692 EDL *edl = new EDL(mwindow->edl);
693 edl->create_objects();
694 Track *track = mwindow->edl->tracks->first;
695 for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
696 int id = track->mixer_id;
697 if( track->data_type != TRACK_AUDIO ) continue;
698 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
699 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
700 mixer_audio->copy_settings(track);
701 mixer_audio->play = 1;
702 mixer_audio->edits->copy_from(track->edits);
703 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
704 autos[AUTOMATION_PAN]->default_auto;
705 pan_auto->values[ch++] = 1.0;
710 EDL *MixersAlign::mixer_master_clip(Track *track)
712 EDL *edl = new EDL(mwindow->edl);
713 edl->create_objects();
714 Track *master_audio = edl->tracks->add_audio_track(0, 0);
715 master_audio->copy_settings(track);
716 master_audio->play = 1;
717 master_audio->edits->copy_from(track->edits);
718 PanAuto* pan_auto = (PanAuto*)master_audio->automation->
719 autos[AUTOMATION_PAN]->default_auto;
720 pan_auto->values[0] = 1.0;
724 int64_t MixersAlign::mixer_tracks_total()
726 int64_t total_len = 0;
727 int64_t sample_rate = mwindow->edl->get_sample_rate();
729 for( ; (m=ma_gui->mixer_list->get_selection_number(0, idx))>=0 ; ++idx ) {
730 Mixer *mixer = mixers[m]->mixer;
731 double render_end = 0;
732 Track *track = mwindow->edl->tracks->first;
733 for( ; track; track=track->next ) {
734 int id = track->mixer_id;
735 if( track->data_type != TRACK_AUDIO ) continue;
736 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
737 double track_end = track->get_length();
738 if( render_end < track_end ) render_end = track_end;
740 if( render_end > audio_end ) render_end = audio_end;
741 double len = render_end - audio_start;
742 if( len > 0 ) total_len += len * sample_rate;
747 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
748 : RenderEngine(0, mwindow->preferences, 0, 0)
750 TransportCommand command;
751 command.command = NORMAL_FWD;
752 command.get_edl()->copy_all(edl);
753 command.change_type = CHANGE_ALL;
754 command.realtime = 0;
755 set_vcache(mwindow->video_cache);
756 set_acache(mwindow->audio_cache);
757 arm_command(&command);
760 MixersAlignARender::~MixersAlignARender()
764 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
766 return arender ? arender->process_buffer(samples, len, pos) : -1;
769 MixersAlignPackage::MixersAlignPackage()
774 MixersAlignPackage::~MixersAlignPackage()
778 MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
779 : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
781 this->dialog = dialog;
782 dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
784 MixersAlignFarm::~MixersAlignFarm()
786 dialog->farming->unlock();
789 LoadPackage* MixersAlignFarm::new_package()
791 return new MixersAlignPackage();
794 void MixersAlignFarm::init_packages()
796 for( int i = 0; i < get_total_packages(); ++i ) {
797 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
798 MixersAlignPackage *package = (MixersAlignPackage *)get_package(i);
799 package->mixer = dialog->mixers[m];
803 LoadClient* MixersAlignFarm::new_client()
805 return new MixersAlignClient(this);
808 MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
813 MixersAlignClient::~MixersAlignClient()
817 void MixersAlignClient::process_package(LoadPackage *pkg)
819 MixersAlignFarm *farm = (MixersAlignFarm *)server;
820 MixersAlign *dialog = farm->dialog;
821 MixersAlignPackage *package = (MixersAlignPackage *)pkg;
822 dialog->process_package(farm, package);
825 static inline void conj_product(int n, double *rp, double *ip,
826 double *arp, double *aip, double *brp, double *bip)
828 for( int i=0; i<n; ++i ) {
829 double ar = arp[i], ai = aip[i];
830 double br = brp[i], bi = -bip[i];
831 rp[i] = ar*br - ai*bi;
832 ip[i] = ar*bi + ai*br;
836 void MixersAlign::process_package(MixersAlignFarm *farm,
837 MixersAlignPackage *package)
839 if( progress->is_cancelled() ) failed = -1;
841 MixersAlignMixer *amix = package->mixer;
842 EDL *edl = mixer_audio_clip(amix->mixer);
844 MixersAlignARender audio(mwindow, edl);
845 int sample_rate = edl->get_sample_rate();
846 int channels = edl->get_audio_channels();
847 int64_t audio_samples = edl->get_audio_samples();
848 int64_t cur_pos = audio_start * sample_rate;
849 int64_t end_pos = audio_end * sample_rate;
850 if( end_pos > audio_samples ) end_pos = audio_samples;
851 int len = master_len, len2 = len/2;
852 double *audio_r = new double[len];
853 double *audio_i = new double[len];
854 Samples *samples[2][MAX_CHANNELS];
855 for( int k=0; k<2; ++k ) {
856 for( int i=0; i<MAX_CHANNELS; ++i )
857 samples[k][i] = i<channels ? new Samples(len2) : 0;
860 int64_t nxt_pos = cur_pos+len2;
861 if( nxt_pos > end_pos ) nxt_pos = end_pos;
862 int len1 = nxt_pos - cur_pos;
863 int ret = audio.render(samples[k], len1, cur_pos);
864 while( !ret && !failed && cur_pos < end_pos ) {
865 update_progress(len1);
866 int64_t pos = cur_pos;
867 cur_pos = nxt_pos; nxt_pos += len2;
868 if( nxt_pos > end_pos ) nxt_pos = end_pos;
869 len1 = nxt_pos - cur_pos;
870 ret = audio.render(samples[1-k], len1, cur_pos);
872 Track *track = edl->tracks->first;
873 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
874 int id = track->mixer_id, atk = atracks.size();
875 while( --atk >= 0 && id != atracks[atk]->track->mixer_id );
876 if( atk < 0 ) continue;
878 double *lp = samples[k][ch]->get_data();
879 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
880 double *np = samples[1-k][ch]->get_data();
881 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
882 while( i < len ) audio_r[i++] = 0;
883 do_fft(len, 0, audio_r, 0, audio_r, audio_i);
884 conj_product(len, audio_r, audio_i,
885 audio_r, audio_i, master_r, master_i);
886 do_fft(len, 1, audio_r, audio_i, audio_r, audio_i);
887 double mx = 0; int64_t mi = -1;
888 for( int i=0; i<len2; ++i ) {
889 double v = fabs(audio_r[i]);
890 if( mx < v ) { mx = v; mi = i + pos; }
893 MixersAlignATrack *atrack= atracks[atk];
894 if( atrack->mx < mx ) {
900 if( progress->is_cancelled() ) failed = -1;
902 if( ret && !failed ) {
903 eprintf("Audio render failed:\n%s", edl->path);
908 for( int k=0; k<2; ++k ) {
909 for( int i=channels; --i>=0; )
910 delete samples[k][i];
916 void MixersAlign::update_progress(int64_t len)
919 total_rendered += len;
920 total_lock->unlock();
921 progress->update(total_rendered);
924 static inline uint64_t high_bit_mask(uint64_t n)
926 n |= n >> 1; n |= n >> 2;
927 n |= n >> 4; n |= n >> 8;
928 n |= n >> 16; n |= n >> 32;
932 void MixersAlign::load_master_audio(Track *track)
934 EDL *edl = mixer_master_clip(track);
935 MixersAlignARender audio(mwindow, edl);
936 int sample_rate = edl->get_sample_rate();
937 int channels = edl->get_audio_channels();
938 int64_t audio_samples = edl->get_audio_samples();
939 int64_t cur_pos = master_start * sample_rate;
940 int64_t end_pos = master_end * sample_rate;
941 if( end_pos > audio_samples ) end_pos = audio_samples;
942 if( cur_pos >= end_pos ) {
943 eprintf(_("master audio track empty"));
947 int64_t audio_len = end_pos - cur_pos;
948 if( audio_len > sample_rate * 60 ) {
949 eprintf(_("master audio track length > 60 seconds"));
953 int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
954 if( master_len != fft_len ) {
955 master_len = fft_len;
956 delete [] master_r; master_r = new double[master_len];
957 delete [] master_i; master_i = new double[master_len];
959 { Samples *samples[MAX_CHANNELS];
960 for( int i=0; i<MAX_CHANNELS; ++i )
961 samples[i] = i<channels ? new Samples(audio_len) : 0;
962 int ret = audio.render(samples, audio_len, cur_pos);
963 if( ret ) failed = 1;
965 double *dp = samples[0]->get_data();
966 int i = 0; double ss = 0;
967 for( ; i<audio_len; ++i ) { ss += *dp * *dp; master_r[i] = *dp++; }
969 for( ; i<fft_len; ++i ) master_r[i] = 0;
970 for( int i=channels; --i>=0; )
973 do_fft(fft_len, 0, master_r, 0, master_r, master_i);
976 void MixersAlign::scan_mixer_audio()
978 for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
980 while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
982 eprintf(_("no mixers selected"));
988 MixersAlignFarm farm(this, n);
989 int64_t total_len = mixer_tracks_total();
990 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
991 progress = mwindow->mainprogress->
992 start_progress(_("Render mixer audio"), total_len);
993 mwindow->gui->unlock_window();
995 farm.process_packages();
996 if( progress->is_cancelled() ) failed = -1;
999 double secs = timer.get_difference()/1000.;
1000 sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1001 mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
1002 progress->update(0);
1003 mwindow->mainprogress->end_progress(progress);
1005 mwindow->gui->show_message(text);
1006 mwindow->gui->unlock_window();
1009 void MixersAlign::update_match()
1011 int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1013 eprintf(_("selection (master) not set"));
1016 master_start = mwindow->edl->local_session->get_inpoint();
1017 if( master_start < 0 ) {
1018 eprintf(_("in point selection (master start) must be set"));
1021 master_end = mwindow->edl->local_session->get_outpoint();
1022 if( master_end < 0 ) {
1023 eprintf(_("out point selection (master end) must be set"));
1026 if( master_start >= master_end ) {
1027 eprintf(_("in/out point selection (master start/end) invalid"));
1030 audio_start = mwindow->edl->local_session->get_selectionstart();
1031 audio_end = mwindow->edl->local_session->get_selectionend();
1032 if( audio_start >= audio_end ) {
1033 eprintf(_("selection (audio start/end) invalid"));
1038 if( !failed ) load_master_audio(mtracks[midx]->track);
1039 if( !failed ) scan_mixer_audio();
1040 if( !failed ) update();
1042 mwindow->gui->lock_window("MixersAlign::update_match");
1043 mwindow->gui->show_message(_("mixer selection match canceled"));
1044 mwindow->gui->unlock_window();
1046 else if( failed > 0 )
1047 eprintf(_("Error in match render."));
1050 void MixersAlign::update()
1052 // mixer best matches
1053 for( int m=0; m<mixers.size(); ++m ) {
1054 MixersAlignMixer *mix = mixers[m];
1055 Mixer *mixer = mix->mixer;
1056 mix->mx = 0; mix->mi = -1; mix->aidx = -1;
1057 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1058 int id = mixer->mixer_ids[i], k = atracks.size();
1059 while( --k >= 0 && atracks[k]->track->mixer_id != id );
1060 if( k < 0 ) continue;
1061 MixersAlignATrack *atrk = atracks[k];
1062 atrk->nudge = atrk->mi < 0 ? 0 :
1063 master_start - atrk->track->from_units(atrk->mi);
1064 if( mix->mx >= atrk->mx ) continue;
1068 mix->nudge = atrk->nudge;
1072 ma_gui->lock_window("MixersAlign::update");
1073 ma_gui->mixer_list->load_list();
1074 ma_gui->mixer_list->set_all_selected(1);
1075 ma_gui->mixer_list->update();
1077 ma_gui->atrack_list->load_list();
1078 for( int m=0; m<mixers.size(); ++m ) {
1079 int aidx = mixers[m]->aidx;
1080 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1082 ma_gui->atrack_list->update();
1083 ma_gui->unlock_window();