2cdf5275ae97c09fda43b386c4d09ffff0527fb1
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mixersalign.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008-2015 Adam Williams <broadcast at earthling dot net>
4  *
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.
9  *
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.
14  *
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
18  *
19  */
20
21 #include "mixersalign.h"
22 #include "auto.h"
23 #include "autos.h"
24 #include "arender.h"
25 #include "clip.h"
26 #include "edit.h"
27 #include "edits.h"
28 #include "edl.h"
29 #include "localsession.h"
30 #include "mainerror.h"
31 #include "mainprogress.h"
32 #include "mwindow.h"
33 #include "mwindowgui.h"
34 #include "panauto.h"
35 #include "panautos.h"
36 #include "preferences.h"
37 #include "track.h"
38 #include "tracks.h"
39 #include "renderengine.h"
40 #include "samples.h"
41 #include "track.h"
42 #include "transportque.h"
43 #include "zwindow.h"
44
45 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
46 {
47         this->mixer = mix;
48         this->nudge = 0;
49         mx = 0;  mi = -1;
50         aidx = -1;
51 }
52
53 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
54         N_("Mixer"), N_("Nudge"),
55 };
56
57 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
58         130, 50,
59 };
60
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)
64 {
65         set_selection_mode(LISTBOX_MULTIPLE);
66         this->dialog = dialog;
67         this->gui = gui;
68         for( int i=MIX_SZ; --i>=0; ) {
69                 col_widths[i] = mix_widths[i];
70                 col_titles[i] = _(mix_titles[i]);
71         }
72 }
73
74 MixersAlignMixerList::~MixersAlignMixerList()
75 {
76         clear();
77 }
78
79 void MixersAlignMixerList::clear()
80 {
81         for( int i=MIX_SZ; --i>=0; )
82                 cols[i].remove_all_objects();
83 }
84
85 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
86 {
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));
94 }
95
96 void MixersAlignMixerList::load_list()
97 {
98         clear();
99         for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
100                 add_mixer(dialog->mixers[i]);
101 }
102
103 void MixersAlignMixerList::update()
104 {
105         int xpos = get_xposition(), ypos = get_yposition();
106         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
107 }
108
109 int MixersAlignMixerList::selection_changed()
110 {
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);
117                         }
118                         mix->aidx = -1;
119                         mix->mx = 0;
120                         mix->mi = -1;
121                         mix->nudge = 0;
122                 }
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;
132                         }
133                         if( idx >= 0 && best ) {
134                                 gui->atrack_list->set_selected(idx, 1);
135                                 MixersAlignMixer *mix = dialog->mixers[m];
136                                 mix->aidx = idx;
137                                 mix->mx = best->mx;
138                                 mix->mi = best->mi;
139                                 mix->nudge = best->nudge;
140                         }
141                         else {
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);
145                                 }
146                         }
147                 }
148         }
149         gui->atrack_list->update();
150
151         load_list();
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);
156         }
157         update();
158         return 0;
159 }
160
161 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
162 {
163         this->track = trk;
164         this->no = no;
165 }
166
167
168 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
169         N_("No"), N_("Mixer"), N_("Track"),
170 };
171
172 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
173         32, 48, 110,
174 };
175
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)
179 {
180         set_selection_mode(LISTBOX_SINGLE);
181         this->dialog = dialog;
182         this->gui = gui;
183         for( int i=MTK_SZ; --i>=0; ) {
184                 col_widths[i] = mtk_widths[i];
185                 col_titles[i] = _(mtk_titles[i]);
186         }
187 }
188
189 MixersAlignMTrackList::~MixersAlignMTrackList()
190 {
191         clear();
192 }
193
194 void MixersAlignMTrackList::clear()
195 {
196         for( int i=MTK_SZ; --i>=0; )
197                 cols[i].remove_all_objects();
198 }
199
200 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
201 {
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));
211 }
212
213 void MixersAlignMTrackList::load_list()
214 {
215         clear();
216         for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
217                 add_mtrack(dialog->mtracks[i]);
218 }
219
220 void MixersAlignMTrackList::update()
221 {
222         int xpos = get_xposition(), ypos = get_yposition();
223         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
224 }
225
226
227 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
228 {
229         this->track = trk;
230         this->no = no;
231         this->nudge = 0;
232         mx = 0;  mi = -1;
233 }
234
235
236 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
237         N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
238 };
239
240 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
241         40, 100, 60, 60, 60,
242 };
243
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)
247 {
248         set_selection_mode(LISTBOX_MULTIPLE);
249         this->dialog = dialog;
250         this->gui = gui;
251         for( int i=ATK_SZ; --i>=0; ) {
252                 col_widths[i] = atk_widths[i];
253                 col_titles[i] = _(atk_titles[i]);
254         }
255 }
256
257 MixersAlignATrackList::~MixersAlignATrackList()
258 {
259         clear();
260 }
261
262 void MixersAlignATrackList::clear()
263 {
264         for( int i=ATK_SZ; --i>=0; )
265                 cols[i].remove_all_objects();
266 }
267
268 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
269 {
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));
285 }
286
287
288 void MixersAlignATrackList::load_list()
289 {
290         clear();
291         for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
292                 add_atrack(dialog->atracks[i]);
293 }
294
295 void MixersAlignATrackList::update()
296 {
297         int xpos = get_xposition(), ypos = get_yposition();
298         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
299 }
300
301 int MixersAlignATrackList::selection_changed()
302 {
303         int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
304         int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
305         if( m >= 0 ) {
306                 int sel = 0;
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);
315                         set_selected(i, 0);
316                         if( i != idx ) continue;
317                         if( is_sel ) {
318                                 mix->aidx = idx;
319                                 mix->mx = atrk->mx;
320                                 mix->mi = atrk->mi;
321                                 mix->nudge = atrk->nudge;
322                                 sel = 1;
323                         }
324                         else {
325                                 mix->aidx = -1;
326                                 mix->mx = 0;
327                                 mix->mi = -1;
328                                 mix->nudge = 0;
329                         }
330                 }
331                 if( sel )
332                         set_selected(idx, 1);
333         }
334         update();
335
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);
342         }
343         gui->mixer_list->update();
344         return 0;
345 }
346
347 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
348                 MixersAlign *dialog, int x, int y)
349  : BC_GenericButton(x, y, _("Reset"))
350 {
351         this->gui = gui;
352         this->dialog = dialog;
353 }
354
355 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
356 {
357         return BC_GenericButton::calculate_w(gui, _("Reset"));
358 }
359
360 int MixersAlignReset::handle_event()
361 {
362         dialog->load_mixers();
363         dialog->load_mtracks();
364         dialog->load_atracks();
365         gui->load_lists();
366         gui->default_selection();
367         return 1;
368 }
369
370 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
371                 MixersAlign *dialog, int x, int y)
372  : BC_GenericButton(x, y, _("Match"))
373 {
374         this->gui = gui;
375         this->dialog = dialog;
376 }
377
378 int MixersAlignMatch::handle_event()
379 {
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();
386         }
387         return 1;
388 }
389
390 MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
391                 MixersAlign *dialog, int x, int y)
392  : BC_GenericButton(x, y, _("Apply"))
393 {
394         this->gui = gui;
395         this->dialog = dialog;
396 }
397
398 int MixersAlignApply::calculate_width(BC_WindowBase *gui)
399 {
400         return BC_GenericButton::calculate_w(gui, _("Apply"));
401 }
402
403 int MixersAlignApply::handle_event()
404 {
405         dialog->apply();
406         return 1;
407 }
408
409 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
410                 MixersAlign *dialog, int x, int y)
411  : BC_GenericButton(x, y, _("Undo"))
412 {
413         this->gui = gui;
414         this->dialog = dialog;
415 }
416
417 int MixersAlignUndo::handle_event()
418 {
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();
425 }
426
427
428 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
429  : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
430 {
431         this->dialog = dialog;
432 }
433 MixersAlignWindow::~MixersAlignWindow()
434 {
435 }
436
437 void MixersAlignWindow::create_objects()
438 {
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;
449         int lh = y2 - y1;
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));
462
463         add_subwindow(new BC_OKButton(this));
464         add_subwindow(new BC_CancelButton(this));
465 }
466
467 int MixersAlignWindow::resize_event(int w, int h)
468 {
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;
476         int lh = y2 - y1;
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);
486         return 0;
487 }
488
489 void MixersAlignWindow::load_lists()
490 {
491         mixer_list->load_list();
492         mtrack_list->load_list();
493         atrack_list->load_list();
494 }
495
496 void MixersAlignWindow::default_selection()
497 {
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);
504                         break;
505                 }
506         }
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);
511         }
512         update_gui();
513 }
514
515 void MixersAlignWindow::update_gui()
516 {
517         mixer_list->update();
518         mtrack_list->update();
519         atrack_list->update();
520 }
521
522
523 MixersAlign::MixersAlign(MWindow *mwindow)
524 {
525         this->mwindow = mwindow;
526         this->ma_gui = 0;
527         farming = new Mutex("MixersAlign::farming");
528         total_lock = new Mutex("MixersAlign::total_lock");
529         thread = new MixersAlignThread(this);
530         total_rendered = 0;
531         master_r = 0;
532         master_i = 0;
533         master_len = 0;
534         progress = 0;
535         failed = 0;
536         undo_edl = 0;
537         master_start = master_end = 0;
538         audio_start = audio_end = 0;
539 }
540
541 MixersAlign::~MixersAlign()
542 {
543         failed = -1;
544         farming->lock("MixersAlign::~MixersAlign");
545         close_window();
546         delete farming;
547         delete thread;
548         delete total_lock;
549         delete progress;
550         delete [] master_r;
551         delete [] master_i;
552 }
553
554 void MixersAlign::start_dialog(int wx, int wy)
555 {
556         this->wx = wx;
557         this->wy = wy;
558         this->undo_edl = new EDL();
559         undo_edl->create_objects();
560         undo_edl->copy_all(mwindow->edl);
561         start();
562 }
563
564 BC_Window *MixersAlign::new_gui()
565 {
566         load_mixers();
567         load_mtracks();
568         load_atracks();
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();
576         return ma_gui;
577 }
578
579 void MixersAlign::apply()
580 {
581         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
582         int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
583         
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;
596                         track->optimize();
597                 }
598         }
599
600         mwindow->gui->lock_window("MixersAlign::handle_done_event");
601         mwindow->update_gui(1);
602         mwindow->gui->unlock_window();
603 }
604
605
606 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
607  : Thread(1, 0, 0)
608 {
609         this->dialog = dialog;
610 }
611 MixersAlignThread::~MixersAlignThread()
612 {
613         join();
614 }
615
616 void MixersAlignThread::run()
617 {
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();
626 }
627
628
629 void MixersAlign::handle_done_event(int result)
630 {
631         if( !result ) {
632                 EDL *edl = mwindow->edl;
633                 mwindow->edl = undo_edl;
634                 mwindow->undo_before();
635                 mwindow->edl = edl;
636                 mwindow->undo_after(_("align mixers"), LOAD_ALL);
637         }
638         else if( thread->running() ) {
639                 failed = -1;
640                 thread->join();
641         }
642 }
643
644 void MixersAlign::handle_close_event(int result)
645 {
646         undo_edl->remove_user();
647         undo_edl = 0;
648         ma_gui = 0;
649 }
650
651 void MixersAlign::load_mixers()
652 {
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]));
657 }
658
659 void MixersAlign::load_mtracks()
660 {
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));
666         }
667 }
668
669 void MixersAlign::load_atracks()
670 {
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));
677         }
678 }
679
680
681 int MixersAlign::mixer_of(Track *track, int &midx)
682 {
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);
686         midx = idx;
687         return k;
688 }
689
690 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
691 {
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;
706         }
707         return edl;
708 }
709
710 EDL *MixersAlign::mixer_master_clip(Track *track)
711 {
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;
721         return edl;
722 }
723
724 int64_t MixersAlign::mixer_tracks_total()
725 {
726         int64_t total_len = 0;
727         int64_t sample_rate = mwindow->edl->get_sample_rate();
728         int m = -1, idx = 0;
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;
739                 }
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;
743         }
744         return total_len;
745 }
746
747 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
748  : RenderEngine(0, mwindow->preferences, 0, 0)
749 {
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);
758 }
759
760 MixersAlignARender::~MixersAlignARender()
761 {
762 }
763
764 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
765 {
766         return arender ? arender->process_buffer(samples, len, pos) : -1;
767 }
768
769 MixersAlignPackage::MixersAlignPackage()
770 {
771         mixer = 0;
772 }
773
774 MixersAlignPackage::~MixersAlignPackage()
775 {
776 }
777
778 MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
779  : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
780 {
781         this->dialog = dialog;
782         dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
783 }
784 MixersAlignFarm::~MixersAlignFarm()
785 {
786         dialog->farming->unlock();
787 }
788
789 LoadPackage* MixersAlignFarm::new_package()
790 {
791         return new MixersAlignPackage();
792 }
793
794 void MixersAlignFarm::init_packages()
795 {
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];
800         }
801 }
802
803 LoadClient* MixersAlignFarm::new_client()
804 {
805         return new MixersAlignClient(this);
806 }
807
808 MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
809  : LoadClient(farm)
810 {
811 }
812
813 MixersAlignClient::~MixersAlignClient()
814 {
815 }
816
817 void MixersAlignClient::process_package(LoadPackage *pkg)
818 {
819         MixersAlignFarm *farm = (MixersAlignFarm *)server;
820         MixersAlign *dialog = farm->dialog;
821         MixersAlignPackage *package = (MixersAlignPackage *)pkg;
822         dialog->process_package(farm, package);
823 }
824
825 static inline void conj_product(int n, double *rp, double *ip,
826                 double *arp, double *aip, double *brp, double *bip)
827 {
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;
833         }
834 }
835
836 void MixersAlign::process_package(MixersAlignFarm *farm,
837                  MixersAlignPackage *package)
838 {
839         if( progress->is_cancelled() ) failed = -1;
840         if( failed ) return;
841         MixersAlignMixer *amix = package->mixer;
842         EDL *edl = mixer_audio_clip(amix->mixer);
843
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;
858         }
859         int k = 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);
871                 if( ret ) break;
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;
877                         int i = 0;
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; }
891                         }
892                         mx /= master_ss;
893                         MixersAlignATrack *atrack= atracks[atk];
894                         if( atrack->mx < mx ) {
895                                 atrack->mx = mx;
896                                 atrack->mi = mi;
897                         }
898                 }
899                 k = 1-k;
900                 if( progress->is_cancelled() ) failed = -1;
901         }
902         if( ret && !failed ) {
903                 eprintf("Audio render failed:\n%s", edl->path);
904                 failed = 1;
905         }
906         delete [] audio_r;
907         delete [] audio_i;
908         for( int k=0; k<2; ++k ) {
909                 for( int i=channels; --i>=0; )
910                         delete samples[k][i];
911         }
912         edl->remove_user();
913 }
914
915
916 void MixersAlign::update_progress(int64_t len)
917 {
918         total_lock->lock();
919         total_rendered += len;
920         total_lock->unlock();
921         progress->update(total_rendered);
922 }
923
924 static inline uint64_t high_bit_mask(uint64_t n)
925 {
926         n |= n >> 1;   n |= n >> 2;
927         n |= n >> 4;   n |= n >> 8;
928         n |= n >> 16;  n |= n >> 32;
929         return n;
930 }
931
932 void MixersAlign::load_master_audio(Track *track)
933 {
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"));
944                 failed = 1;
945                 return;
946         }
947         int64_t audio_len = end_pos - cur_pos;
948         if( audio_len > sample_rate * 60 ) {
949                 eprintf(_("master audio track length > 60 seconds"));
950                 failed = 1;
951                 return;
952         }
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];
958         }
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;
964                 edl->remove_user();
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++; }
968                 master_ss = ss;
969                 for( ; i<fft_len; ++i ) master_r[i] = 0;
970                 for( int i=channels; --i>=0; )
971                         delete samples[i];
972         }
973         do_fft(fft_len, 0, master_r, 0, master_r, master_i);
974 }
975
976 void MixersAlign::scan_mixer_audio()
977 {
978         for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
979         int n = 0;
980         while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
981         if( !n ) {
982                 eprintf(_("no mixers selected"));
983                 return;
984         }
985
986         Timer timer;
987         total_rendered = 0;
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();
994
995         farm.process_packages();
996         if( progress->is_cancelled() ) failed = -1;
997
998         char text[BCSTRLEN];
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);
1004         progress = 0;
1005         mwindow->gui->show_message(text);
1006         mwindow->gui->unlock_window();
1007 }
1008
1009 void MixersAlign::update_match()
1010 {
1011         int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1012         if( midx < 0 ) {
1013                 eprintf(_("selection (master) not set"));
1014                 return;
1015         }
1016         master_start = mwindow->edl->local_session->get_inpoint();
1017         if( master_start < 0 ) {
1018                 eprintf(_("in point selection (master start) must be set"));
1019                 return;
1020         }
1021         master_end = mwindow->edl->local_session->get_outpoint();
1022         if( master_end < 0 ) {
1023                 eprintf(_("out point selection (master end) must be set"));
1024                 return;
1025         }
1026         if( master_start >= master_end ) {
1027                 eprintf(_("in/out point selection (master start/end) invalid"));
1028                 return;
1029         }
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"));
1034                 return;
1035         }
1036
1037         failed = 0;
1038         if( !failed ) load_master_audio(mtracks[midx]->track);
1039         if( !failed ) scan_mixer_audio();
1040         if( !failed ) update();
1041         if( failed < 0 ) {
1042                 mwindow->gui->lock_window("MixersAlign::update_match");
1043                 mwindow->gui->show_message(_("mixer selection match canceled"));
1044                 mwindow->gui->unlock_window();
1045         }
1046         else if( failed > 0 )
1047                 eprintf(_("Error in match render."));
1048 }
1049
1050 void MixersAlign::update()
1051 {
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;
1065                         mix->aidx = k;
1066                         mix->mx = atrk->mx;
1067                         mix->mi = atrk->mi;
1068                         mix->nudge = atrk->nudge;
1069                 }
1070         }
1071
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();
1076
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);
1081         }
1082         ma_gui->atrack_list->update();
1083         ma_gui->unlock_window();
1084 }
1085