add libdav1d codec, add remap_a/v_codec option keywords
[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 "edlsession.h"
30 #include "localsession.h"
31 #include "mainerror.h"
32 #include "mainprogress.h"
33 #include "mwindow.h"
34 #include "mwindowgui.h"
35 #include "panauto.h"
36 #include "panautos.h"
37 #include "preferences.h"
38 #include "track.h"
39 #include "tracks.h"
40 #include "renderengine.h"
41 #include "samples.h"
42 #include "track.h"
43 #include "transportque.h"
44 #include "zwindow.h"
45
46 // c = corr(a,b): A=fft(a),B=fft(b) C=A*conj(B) c=ifft(C)
47 static inline void conj_product(int n, double *rp, double *ip,
48                 double *arp, double *aip, double *brp, double *bip)
49 {
50         for( int i=0; i<n; ++i ) {
51                 double ar = arp[i], ai = aip[i];
52                 double br = brp[i], bi = -bip[i];
53                 rp[i] = ar*br - ai*bi;
54                 ip[i] = ar*bi + ai*br;
55         }
56 }
57
58 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
59 {
60         this->mixer = mix;
61         this->nudge = 0;
62         mx = 0;  mi = -1;
63         br = 0;  bi = 0;
64         aidx = -1;
65 }
66 MixersAlignMixer::~MixersAlignMixer()
67 {
68         delete [] br;
69         delete [] bi;
70 }
71
72 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
73         N_("Mixer"), N_("Nudge"),
74 };
75
76 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
77         130, 50,
78 };
79
80 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
81                 MixersAlign *dialog, int x, int y, int w, int h)
82  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
83 {
84         set_selection_mode(LISTBOX_MULTIPLE);
85         this->dialog = dialog;
86         this->gui = gui;
87         for( int i=MIX_SZ; --i>=0; ) {
88                 col_widths[i] = mix_widths[i];
89                 col_titles[i] = _(mix_titles[i]);
90         }
91 }
92
93 MixersAlignMixerList::~MixersAlignMixerList()
94 {
95         clear();
96 }
97
98 void MixersAlignMixerList::clear()
99 {
100         for( int i=MIX_SZ; --i>=0; )
101                 cols[i].remove_all_objects();
102 }
103
104 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
105 {
106         char mixer_text[BCSTRLEN];
107         snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
108                 mixer->mixer->idx, mixer->mixer->title);
109         cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
110         char nudge_text[BCSTRLEN];
111         sprintf(nudge_text, _("%0.4f"), mixer->nudge);
112         cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
113 }
114
115 void MixersAlignMixerList::load_list()
116 {
117         clear();
118         for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
119                 add_mixer(dialog->mixers[i]);
120 }
121
122 void MixersAlignMixerList::update()
123 {
124         int xpos = get_xposition(), ypos = get_yposition();
125         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
126 }
127
128 int MixersAlignMixerList::selection_changed()
129 {
130         for( int m=0; m<dialog->mixers.size(); ++m ) {
131                 MixersAlignMixer *mix = dialog->mixers[m];
132                 if( !is_selected(m) ) {
133                         for( int i=0; i<dialog->atracks.size(); ++i ) {
134                                 if( m != dialog->amixer_of(i) ) continue;
135                                 gui->atrack_list->set_selected(i, 0);
136                         }
137                         mix->aidx = -1;
138                         mix->mx = 0;
139                         mix->mi = -1;
140                         mix->nudge = 0;
141                 }
142                 else if( mix->aidx < 0 ) {
143                         MixersAlignATrack *best = 0;  int idx = -1;
144                         for( int i=0; i<dialog->atracks.size(); ++i ) {
145                                 if( m != dialog->amixer_of(i) ) continue;
146                                 MixersAlignATrack *atrk = dialog->atracks[i];
147                                 if( atrk->mi < 0 ) continue;
148                                 gui->atrack_list->set_selected(i, 0);
149                                 if( best && best->mx >= atrk->mx ) continue;
150                                 best = atrk;  idx = i;
151                         }
152                         if( idx >= 0 && best ) {
153                                 gui->atrack_list->set_selected(idx, 1);
154                                 MixersAlignMixer *mix = dialog->mixers[m];
155                                 mix->aidx = idx;
156                                 mix->mx = best->mx;
157                                 mix->mi = best->mi;
158                                 mix->nudge = best->nudge;
159                         }
160                         else {
161                                 for( int i=0; i<dialog->atracks.size(); ++i ) {
162                                         if( m != dialog->amixer_of(i) ) continue;
163                                         gui->atrack_list->set_selected(i, 1);
164                                 }
165                         }
166                 }
167         }
168         gui->atrack_list->update();
169
170         load_list();
171         for( int i=0; i<dialog->atracks.size(); ++i ) {
172                 if( !gui->atrack_list->is_selected(i) ) continue;
173                 int m = dialog->amixer_of(i);
174                 if( m >= 0 ) set_selected(m, 1);
175         }
176         update();
177         return 0;
178 }
179
180 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
181 {
182         this->track = trk;
183         this->no = no;
184 }
185
186
187 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
188         N_("No"), N_("Mixer"), N_("Track"),
189 };
190
191 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
192         32, 48, 110,
193 };
194
195 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
196                 MixersAlign *dialog, int x, int y, int w, int h)
197  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
198 {
199         set_selection_mode(LISTBOX_SINGLE);
200         this->dialog = dialog;
201         this->gui = gui;
202         for( int i=MTK_SZ; --i>=0; ) {
203                 col_widths[i] = mtk_widths[i];
204                 col_titles[i] = _(mtk_titles[i]);
205         }
206 }
207
208 MixersAlignMTrackList::~MixersAlignMTrackList()
209 {
210         clear();
211 }
212
213 void MixersAlignMTrackList::clear()
214 {
215         for( int i=MTK_SZ; --i>=0; )
216                 cols[i].remove_all_objects();
217 }
218
219 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
220 {
221         char no_text[BCSTRLEN];
222         snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
223         cols[MTK_NO].append(new BC_ListBoxItem(no_text));
224         char mixer_text[BCSTRLEN];
225         Track *track = mtrk->track;
226         int k = -1, m = dialog->mixer_of(track, k);
227         snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,k);
228         cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
229         cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
230 }
231
232 void MixersAlignMTrackList::load_list()
233 {
234         clear();
235         for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
236                 add_mtrack(dialog->mtracks[i]);
237 }
238
239 void MixersAlignMTrackList::update()
240 {
241         int xpos = get_xposition(), ypos = get_yposition();
242         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
243 }
244
245
246 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
247 {
248         this->track = trk;
249         this->no = no;
250         this->nudge = 0;
251         this->ss = 0;
252         mx = 0;  mi = -1;
253 }
254
255 MixersAlignATrack::~MixersAlignATrack()
256 {
257 }
258
259
260 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
261         N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
262 };
263
264 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
265         40, 100, 60, 60, 60,
266 };
267
268 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
269                 MixersAlign *dialog, int x, int y, int w, int h)
270  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
271 {
272         set_selection_mode(LISTBOX_MULTIPLE);
273         this->dialog = dialog;
274         this->gui = gui;
275         for( int i=ATK_SZ; --i>=0; ) {
276                 col_widths[i] = atk_widths[i];
277                 col_titles[i] = _(atk_titles[i]);
278         }
279 }
280
281 MixersAlignATrackList::~MixersAlignATrackList()
282 {
283         clear();
284 }
285
286 void MixersAlignATrackList::clear()
287 {
288         for( int i=ATK_SZ; --i>=0; )
289                 cols[i].remove_all_objects();
290 }
291
292 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
293 {
294         char atrack_text[BCSTRLEN];
295         Track *track = atrack->track;
296         int m = dialog->mixer_of(track);
297         snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
298         cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
299         cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
300         char nudge_text[BCSTRLEN];
301         sprintf(nudge_text, "%0.4f", atrack->nudge);
302         cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
303         char mx_text[BCSTRLEN];
304         sprintf(mx_text, "%0.4f", atrack->mx);
305         cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
306         char mi_text[BCSTRLEN];
307         sprintf(mi_text, "%jd", atrack->mi);
308         cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
309 }
310
311
312 void MixersAlignATrackList::load_list()
313 {
314         clear();
315         for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
316                 add_atrack(dialog->atracks[i]);
317 }
318
319 void MixersAlignATrackList::update()
320 {
321         int xpos = get_xposition(), ypos = get_yposition();
322         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
323 }
324
325 int MixersAlignATrackList::selection_changed()
326 {
327         int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
328         int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
329         if( m >= 0 ) {
330                 int sel = 0;
331                 MixersAlignMixer *mix = dialog->mixers[m];
332                 for( int i=0; i<dialog->atracks.size(); ++i ) {
333                         int k = dialog->amixer_of(i);
334                         if( k < 0 ) { set_selected(i, 0);  continue; }
335                         if( m != k ) continue;
336                         MixersAlignATrack *atrk = dialog->atracks[i];
337                         if( atrk->mi < 0 ) continue;
338                         int is_sel = is_selected(i);
339                         set_selected(i, 0);
340                         if( i != idx ) continue;
341                         if( is_sel ) {
342                                 mix->aidx = idx;
343                                 mix->mx = atrk->mx;
344                                 mix->mi = atrk->mi;
345                                 mix->nudge = atrk->nudge;
346                                 sel = 1;
347                         }
348                         else {
349                                 mix->aidx = -1;
350                                 mix->mx = 0;
351                                 mix->mi = -1;
352                                 mix->nudge = 0;
353                         }
354                 }
355                 if( sel )
356                         set_selected(idx, 1);
357         }
358         update();
359
360         gui->mixer_list->load_list();
361         for( int i=0; i<dialog->atracks.size(); ++i ) {
362                 if( !is_selected(i) ) continue;
363                 int m = dialog->amixer_of(i);
364                 if( m < 0 ) continue;
365                 gui->mixer_list->set_selected(m, 1);
366         }
367         gui->mixer_list->update();
368         return 0;
369 }
370
371 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
372                 MixersAlign *dialog, int x, int y)
373  : BC_GenericButton(x, y, _("Reset"))
374 {
375         this->gui = gui;
376         this->dialog = dialog;
377 }
378
379 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
380 {
381         return BC_GenericButton::calculate_w(gui, _("Reset"));
382 }
383
384 int MixersAlignReset::handle_event()
385 {
386         dialog->load_mixers();
387         dialog->load_mtracks();
388         dialog->load_atracks();
389         gui->load_lists();
390         gui->default_selection();
391         return 1;
392 }
393
394 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
395                 MixersAlign *dialog, int x, int y)
396  : BC_GenericButton(x, y, _("Match"))
397 {
398         this->gui = gui;
399         this->dialog = dialog;
400 }
401
402 int MixersAlignMatch::handle_event()
403 {
404         if( !dialog->thread->running() ) {
405                 dialog->thread->start(1);
406         }
407         return 1;
408 }
409
410 MixersAlignMatchAll::MixersAlignMatchAll(MixersAlignWindow *gui,
411                 MixersAlign *dialog, int x, int y)
412  : BC_GenericButton(x, y, _("Match All"))
413 {
414         this->gui = gui;
415         this->dialog = dialog;
416 }
417
418 int MixersAlignMatchAll::handle_event()
419 {
420         if( !dialog->thread->running() ) {
421                 dialog->thread->start(0);
422         }
423         return 1;
424 }
425
426 MixersAlignNudgeTracks::MixersAlignNudgeTracks(MixersAlignWindow *gui,
427                 MixersAlign *dialog, int x, int y)
428  : BC_GenericButton(x, y, _("Apply"))
429 {
430         this->gui = gui;
431         this->dialog = dialog;
432 }
433
434 int MixersAlignNudgeTracks::calculate_width(BC_WindowBase *gui)
435 {
436         return BC_GenericButton::calculate_w(gui, _("Apply"));
437 }
438
439 int MixersAlignNudgeTracks::handle_event()
440 {
441         dialog->nudge_tracks();
442         return 1;
443 }
444
445 MixersAlignNudgeSelected::MixersAlignNudgeSelected(MixersAlignWindow *gui,
446                 MixersAlign *dialog, int x, int y)
447  : BC_GenericButton(x, y, _("Move"))
448 {
449         this->gui = gui;
450         this->dialog = dialog;
451 }
452
453 int MixersAlignNudgeSelected::calculate_width(BC_WindowBase *gui)
454 {
455         return BC_GenericButton::calculate_w(gui, _("Move"));
456 }
457
458 int MixersAlignNudgeSelected::handle_event()
459 {
460         dialog->nudge_selected();
461         return 1;
462 }
463
464 MixersAlignUndoItem::MixersAlignUndoItem(const char *text, int no)
465  : BC_MenuItem(text)
466 {
467         this->no = no;
468 }
469 MixersAlignUndoItem::~MixersAlignUndoItem()
470 {
471 }
472
473 int MixersAlignUndoItem::handle_event()
474 {
475         MixersAlignUndo *undo = (MixersAlignUndo *)get_popup_menu();
476         undo->dialog->apply_undo(no);
477         return 1;
478 }
479
480 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
481                 MixersAlign *dialog, int x, int y)
482  : BC_PopupMenu(x, y, 100, _("Undo"))
483 {
484         this->gui = gui;
485         this->dialog = dialog;
486 }
487 MixersAlignUndo::~MixersAlignUndo()
488 {
489 }
490
491 void MixersAlignUndo::create_objects()
492 {
493         add_undo_item(0);
494 }
495
496 void MixersAlignUndo::add_undo_item(int no)
497 {
498         char text[BCSTRLEN];
499         if( no > 0 )
500                 sprintf(text, _("chkpt %d"), no);
501         else
502                 sprintf(text, _("start over"));
503         add_item(new MixersAlignUndoItem(text, no));
504 }
505
506 MixersAlignCheckPoint::MixersAlignCheckPoint(MixersAlignWindow *gui,
507                 MixersAlign *dialog, int x, int y)
508  : BC_GenericButton(x, y, 100, _("CheckPoint"))
509 {
510         this->gui = gui;
511         this->dialog = dialog;
512 }
513
514 int MixersAlignCheckPoint::handle_event()
515 {
516         dialog->check_point();
517         return 1;
518 }
519
520 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
521  : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
522 {
523         this->dialog = dialog;
524 }
525 MixersAlignWindow::~MixersAlignWindow()
526 {
527 }
528
529 void MixersAlignWindow::create_objects()
530 {
531         int x = 10, y = 10, w4 = (get_w()-x-10)/4, lw = w4 + 20;
532         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
533         mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
534         add_subwindow(mixer_title);
535         mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
536         add_subwindow(mtrack_title);
537         atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
538         add_subwindow(atrack_title);
539         y += mixer_title->get_h() + 10;
540         int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 32;
541         int lh = y2 - y1;
542         mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
543         add_subwindow(mixer_list);
544         mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-20, lh);
545         add_subwindow(mtrack_list);
546         atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
547         add_subwindow(atrack_list);
548         int xr = x2-10 - MixersAlignReset::calculate_width(this);
549         y1 = y2+20;
550         add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y1));
551         add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y1));
552         int xa = x3-10 - MixersAlignNudgeTracks::calculate_width(this);
553         add_subwindow(nudge_tracks = new MixersAlignNudgeTracks(this, dialog, xa, y1));
554         y2 = y1 + nudge_tracks->get_h() + 10;
555         add_subwindow(match_all = new MixersAlignMatchAll(this, dialog, xr, y2));
556         add_subwindow(nudge_selected = new MixersAlignNudgeSelected(this, dialog, xa, y2));
557         int xu = x3+10;
558         add_subwindow(check_point = new MixersAlignCheckPoint(this, dialog, xu, y1));
559         add_subwindow(undo = new MixersAlignUndo(this, dialog, xu, y2));
560         undo->create_objects();
561
562         add_subwindow(new BC_OKButton(this));
563         add_subwindow(new BC_CancelButton(this));
564 }
565
566 int MixersAlignWindow::resize_event(int w, int h)
567 {
568         int x = 10, y = 10, w4 = (w-x-10)/4, lw = w4 + 20;
569         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
570         mixer_title->reposition_window(x1, y);
571         mtrack_title->reposition_window(x2, y);
572         atrack_title->reposition_window(x3, y);
573         y += mixer_title->get_h() + 10;
574         int y1 = y, y2 = h - BC_OKButton::calculate_h() - 32;
575         int lh = y2 - y1;
576         mixer_list->reposition_window(x1, y, x2-x1-20, lh);
577         mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
578         atrack_list->reposition_window(x3, y, x4-x3-20, lh);
579         int xr = x2-10 - MixersAlignReset::calculate_width(this);
580         y1 = y2+20;
581         reset->reposition_window(xr, y1);
582         match->reposition_window(x2+10, y1);
583         int xa = x3-10 - MixersAlignNudgeTracks::calculate_width(this);
584         nudge_tracks->reposition_window(xa, y1);
585         y2 = y1 + nudge_tracks->get_h() + 10;
586         match_all->reposition_window(xr, y2);
587         nudge_selected->reposition_window(xa, y2);
588         int xu = x3+10;
589         check_point->reposition_window(xu, y1);
590         undo->reposition_window(xu, y2);
591         return 0;
592 }
593
594 void MixersAlignWindow::load_lists()
595 {
596         mixer_list->load_list();
597         mtrack_list->load_list();
598         atrack_list->load_list();
599 }
600
601 void MixersAlignWindow::default_selection()
602 {
603 // mixers selects all mixers
604         mixer_list->set_all_selected(1);
605 // master selects first mixer audio track
606         for( int i=0; i<dialog->mtracks.size(); ++i ) {
607                 if( dialog->mmixer_of(i) >= 0 ) {
608                         mtrack_list->set_selected(i, 1);
609                         break;
610                 }
611         }
612 // audio selects all mixer audio tracks
613         for( int i=0; i<dialog->atracks.size(); ++i ) {
614                 if( dialog->amixer_of(i) >= 0 )
615                         atrack_list->set_selected(i, 1);
616         }
617         update_gui();
618 }
619
620 void MixersAlignWindow::update_gui()
621 {
622         mixer_list->update();
623         mtrack_list->update();
624         atrack_list->update();
625 }
626
627
628 MixersAlign::MixersAlign(MWindow *mwindow)
629 {
630         this->mwindow = mwindow;
631         this->ma_gui = 0;
632         farming = new Mutex("MixersAlign::farming");
633         total_lock = new Mutex("MixersAlign::total_lock");
634         thread = new MixersAlignThread(this);
635         total_rendered = 0;
636         master_r = 0;
637         master_i = 0;
638         master_len = 0;
639         sample_len = 0x10000;
640         progress = 0;
641         failed = 0;
642         master_start = master_end = 0;
643         audio_start = audio_end = 0;
644 }
645
646 MixersAlign::~MixersAlign()
647 {
648         failed = -1;
649         farming->lock("MixersAlign::~MixersAlign");
650         close_window();
651         delete farming;
652         delete thread;
653         delete total_lock;
654         delete progress;
655         delete [] master_r;
656         delete [] master_i;
657 }
658
659 void MixersAlign::start_dialog(int wx, int wy)
660 {
661         this->wx = wx;
662         this->wy = wy;
663         EDL *start_over = new EDL();
664         start_over->create_objects();
665         start_over->copy_all(mwindow->edl);
666         undo_edls.append(start_over);
667         start();
668 }
669
670 BC_Window *MixersAlign::new_gui()
671 {
672         load_mixers();
673         load_mtracks();
674         load_atracks();
675         ma_gui = new MixersAlignWindow(this, wx, wy);
676         ma_gui->lock_window("MixersAlign::new_gui");
677         ma_gui->create_objects();
678         ma_gui->load_lists();
679         ma_gui->default_selection();
680         ma_gui->show_window(1);
681         ma_gui->unlock_window();
682         return ma_gui;
683 }
684
685 // shift armed mixer tracks by nudge
686 void MixersAlign::nudge_tracks()
687 {
688         mwindow->gui->lock_window("MixersAlign::apply_tracks");
689         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
690         int midx = mmixer_of(idx);
691         EDL *edl = mwindow->edl;
692         
693         for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
694                 if( m == midx ) continue;  // master does not move
695                 MixersAlignMixer *mix = mixers[m];
696                 Mixer *mixer = mix->mixer;
697                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
698                         int id = mixer->mixer_ids[i];
699                         Track *track = edl->tracks->first;
700                         while( track && track->mixer_id != id ) track = track->next;
701                         if( !track ) continue;
702                         double nudge = mix->nudge;
703                         int record = track->record;  track->record = 1;
704                         if( nudge < 0 ) {
705                                 track->clear(0, -nudge, 1,
706                                         edl->session->labels_follow_edits,
707                                         edl->session->plugins_follow_edits,
708                                         edl->session->autos_follow_edits, 0);
709                         }
710                         else if( nudge > 0 ) {
711                                 track->paste_silence(0, nudge,
712                                         edl->session->plugins_follow_edits,
713                                         edl->session->autos_follow_edits);
714                         }
715                         track->record = record;
716                 }
717         }
718         edl->optimize();
719
720         mwindow->update_gui(1);
721         mwindow->gui->unlock_window();
722         clear_mixer_nudge();
723 }
724
725 // move selected mixer edits by nudge
726 void MixersAlign::nudge_selected()
727 {
728         mwindow->gui->lock_window("MixersAlign::apply_selected");
729         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
730         int midx = mmixer_of(idx);
731         EDL *edl = mwindow->edl;
732
733         ArrayList<int> track_arms;  // ugly
734         for( Track *track=edl->tracks->first; track; track=track->next ) {
735                 track_arms.append(track->record);
736                 track->record = 0;
737         }
738         for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
739                 if( m == midx ) continue;  // master does not move
740                 MixersAlignMixer *mix = mixers[m];
741                 Mixer *mixer = mix->mixer;
742                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
743                         int id = mixer->mixer_ids[i];
744                         Track *track = edl->tracks->first;
745                         while( track && track->mixer_id != id ) track = track->next;
746                         if( !track ) continue;
747                         double nudge = mix->nudge;
748                         track->record = 1;
749                         double position = 0;  Track *first_track = 0;
750                         EDL *clip = edl->selected_edits_to_clip(0, &position, &first_track);
751                         if( clip ) {
752                                 Track *clip_track = clip->tracks->first;
753                                 Track *edl_track = first_track;
754                                 while( clip_track && edl_track ) {
755                                         Edit *edit = clip_track->edits->first;
756                                         for( ; edit; edit=edit->next ) {
757                                                 double start = clip_track->from_units(edit->startproject);
758                                                 double end = clip_track->from_units(edit->startproject+edit->length);
759                                                 start += position;  end += position;
760                                                 edl_track->clear(start, end, 1,
761                                                         edl->session->labels_follow_edits,
762                                                         edl->session->plugins_follow_edits,
763                                                         edl->session->autos_follow_edits, 0);
764                                                 edl_track->paste_silence(start, end,
765                                                         edl->session->plugins_follow_edits,
766                                                         edl->session->autos_follow_edits);
767                                         }
768                                         clip_track = clip_track->next;
769                                         edl_track = edl_track->next;
770                                 }
771                                 position += nudge;
772                                 edl->paste_edits(clip, first_track, position, 1);
773                         }
774                         track->record = 0;
775                 }
776         }
777         int i = 0;
778         for( Track *track=edl->tracks->first; track; track=track->next )
779                 track->record = track_arms[i++];
780         edl->optimize();
781
782         mwindow->update_gui(1);
783         mwindow->gui->unlock_window();
784         clear_mixer_nudge();
785 }
786
787
788 void MixersAlign::clear_mixer_nudge()
789 {
790 // so pressing apply twice does not damage the result
791         for( int m=0; m<mixers.size(); ++m )
792                 mixers[m]->nudge = 0;
793         ma_gui->mixer_list->load_list();
794         ma_gui->lock_window("MixersAlign::clear_mixer_nudge");
795         ma_gui->mixer_list->update();
796         ma_gui->unlock_window();
797 }
798
799 void MixersAlign::check_point()
800 {
801         mwindow->gui->lock_window("MixersAlign::check_point");
802         ma_gui->undo->add_undo_item(undo_edls.size());
803         EDL *undo_edl = new EDL();
804         undo_edl->create_objects();
805         undo_edl->copy_all(mwindow->edl);
806         undo_edls.append(undo_edl);
807         mwindow->gui->unlock_window();
808 }
809
810
811 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
812  : Thread(1, 0, 0)
813 {
814         this->dialog = dialog;
815 }
816 MixersAlignThread::~MixersAlignThread()
817 {
818         join();
819 }
820
821 void MixersAlignThread::start(int fwd)
822 {
823         this->fwd = fwd;
824         MixersAlignWindow *gui = dialog->ma_gui;
825         gui->reset->disable();
826         gui->match->disable();
827         gui->match_all->disable();
828         gui->nudge_tracks->disable();
829         gui->nudge_selected->disable();
830         gui->check_point->disable();
831 //      gui->undo->disable();
832         Thread::start();
833 }
834
835 void MixersAlignThread::run()
836 {
837         if( fwd )
838                 dialog->match_fwd();
839         else
840                 dialog->match_rev();
841         MixersAlignWindow *gui = dialog->ma_gui;
842         gui->lock_window("MixersAlignThread::run");
843         gui->reset->enable();
844         gui->match->enable();
845         gui->match_all->enable();
846         gui->nudge_tracks->enable();
847         gui->nudge_selected->enable();
848         gui->check_point->enable();
849 //      gui->undo->enable();
850         gui->unlock_window();
851 }
852
853
854 void MixersAlign::handle_done_event(int result)
855 {
856         if( thread->running() ) {
857                 failed = -1;
858                 thread->join();
859         }
860         if( !result ) {
861                 mwindow->gui->lock_window("MixersAlign::handle_done_event");
862                 EDL *edl = mwindow->edl;
863                 mwindow->edl = undo_edls[0];
864                 mwindow->undo_before();
865                 mwindow->edl = edl;
866                 mwindow->undo_after(_("align mixers"), LOAD_ALL);
867                 mwindow->gui->unlock_window();
868         }
869 }
870
871 void MixersAlign::handle_close_event(int result)
872 {
873         ma_gui = 0;
874         mixers.clear();
875         mtracks.clear();
876         atracks.clear();
877         undo_edls.clear();
878 }
879
880 void MixersAlign::load_mixers()
881 {
882         mixers.clear();
883         Mixers &edl_mixers = mwindow->edl->mixers;
884         for( int i=0; i<edl_mixers.size(); ++i )
885                 mixers.append(new MixersAlignMixer(edl_mixers[i]));
886 }
887
888 void MixersAlign::load_mtracks()
889 {
890         mtracks.clear();
891         Track *track=mwindow->edl->tracks->first;
892         for( int no=0; track; ++no, track=track->next ) {
893                 if( track->data_type != TRACK_AUDIO ) continue;
894                 mtracks.append(new MixersAlignMTrack(track, no));
895         }
896 }
897
898 void MixersAlign::load_atracks()
899 {
900         atracks.clear();
901         Track *track=mwindow->edl->tracks->first;
902         for( int no=0; track; ++no, track=track->next ) {
903                 if( track->data_type != TRACK_AUDIO ) continue;
904                 if( mixer_of(track) < 0 ) continue;
905                 atracks.append(new MixersAlignATrack(track, no));
906         }
907 }
908
909
910 int MixersAlign::atrack_of(MixersAlignMixer *mix, int ch)
911 {
912         int k = -1;
913         Mixer *mixer = mix->mixer;
914         for( int i=0,n=mixer->mixer_ids.size(); i<n; ++i ) {
915                 int id = mixer->mixer_ids[i];  k = atracks.size();
916                 while( --k >= 0 && atracks[k]->track->mixer_id != id );
917                 if( k < 0 ) continue;
918                 if( --ch < 0 ) break;
919         }
920         return k;
921 }
922
923 int MixersAlign::mixer_of(Track *track, int &midx)
924 {
925         int id = track->mixer_id, k = mixers.size(), idx = -1;
926         while( idx < 0 && --k >= 0 )
927                 idx = mixers[k]->mixer->mixer_ids.number_of(id);
928         midx = idx;
929         return k;
930 }
931
932
933 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
934 {
935         EDL *edl = new EDL(mwindow->edl);
936         edl->create_objects();
937         Track *track = mwindow->edl->tracks->first;
938         for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
939                 int id = track->mixer_id;
940                 if( track->data_type != TRACK_AUDIO ) continue;
941                 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
942                 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
943                 mixer_audio->copy_settings(track);
944                 mixer_audio->play = 1;
945                 mixer_audio->edits->copy_from(track->edits);
946                 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
947                                 autos[AUTOMATION_PAN]->default_auto;
948                 pan_auto->values[ch++] = 1.0;
949         }
950         return edl;
951 }
952
953 EDL *MixersAlign::mixer_master_clip(Track *track)
954 {
955         EDL *edl = new EDL(mwindow->edl);
956         edl->create_objects();
957         Track *master_audio = edl->tracks->add_audio_track(0, 0);
958         master_audio->copy_settings(track);
959         master_audio->play = 1;
960         master_audio->edits->copy_from(track->edits);
961         PanAuto* pan_auto = (PanAuto*)master_audio->automation->
962                         autos[AUTOMATION_PAN]->default_auto;
963         pan_auto->values[0] = 1.0;
964         return edl;
965 }
966
967 int64_t MixersAlign::mixer_tracks_total(int midx)
968 {
969         int64_t total_len = 0;
970         int64_t sample_rate = mwindow->edl->get_sample_rate();
971         int m = -1;
972         for( int i=0; (m=ma_gui->mixer_list->get_selection_number(0, i))>=0 ; ++i ) {
973                 if( m == midx ) continue;
974                 Mixer *mixer = mixers[m]->mixer;
975                 double render_end = 0;
976                 Track *track = mwindow->edl->tracks->first;
977                 for( ; track; track=track->next ) {
978                         int id = track->mixer_id;
979                         if( track->data_type != TRACK_AUDIO ) continue;
980                         if( mixer->mixer_ids.number_of(id) < 0 ) continue;
981                         double track_end = track->get_length();
982                         if( render_end < track_end ) render_end = track_end;
983                 }
984                 if( render_end > audio_end ) render_end = audio_end;
985                 double len = render_end - audio_start;
986                 if( len > 0 ) total_len += len * sample_rate;
987         }
988         return total_len;
989 }
990
991 void MixersAlign::apply_undo(int no)
992 {
993         if( thread->running() ) {
994                 failed = -1;
995                 thread->join();
996         }
997         mwindow->gui->lock_window("MixersAlignUndo::handle_event");
998         EDL *undo_edl = undo_edls[no];
999         mwindow->edl->copy_all(undo_edl);
1000         mwindow->update_gui(1);
1001         mwindow->gui->unlock_window();
1002         ma_gui->reset->handle_event();
1003 }
1004
1005 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
1006  : RenderEngine(0, mwindow->preferences, 0, 0)
1007 {
1008         TransportCommand command;
1009         command.command = NORMAL_FWD;
1010         command.get_edl()->copy_all(edl);
1011         command.change_type = CHANGE_ALL;
1012         command.realtime = 0;
1013         set_vcache(mwindow->video_cache);
1014         set_acache(mwindow->audio_cache);
1015         arm_command(&command);
1016 }
1017
1018 MixersAlignARender::~MixersAlignARender()
1019 {
1020 }
1021
1022 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
1023 {
1024         return arender ? arender->process_buffer(samples, len, pos) : -1;
1025 }
1026
1027 // scan mixer tracks for best target
1028 MixersAlignScanFarm::MixersAlignScanFarm(MixersAlign *dialog, int cpus, int n)
1029  : LoadServer(cpus, n)
1030 {
1031         dialog->farming->lock("MixersAlignScanFarm::MixersAlignScanFarm");
1032         this->dialog = dialog;
1033         this->len = len;
1034 }
1035 MixersAlignScanFarm::~MixersAlignScanFarm()
1036 {
1037         dialog->farming->unlock();
1038 }
1039
1040 MixersAlignScanPackage::MixersAlignScanPackage(MixersAlignScanFarm *farm)
1041 {
1042         mixer = 0;
1043 }
1044 MixersAlignScanPackage::~MixersAlignScanPackage()
1045 {
1046 }
1047
1048 LoadPackage* MixersAlignScanFarm::new_package()
1049 {
1050         return new MixersAlignScanPackage(this);
1051 }
1052
1053 void MixersAlignScanFarm::init_packages()
1054 {
1055         int idx = dialog->ma_gui->mtrack_list->get_selection_number(0, 0);
1056         int midx = dialog->mmixer_of(idx);
1057         for( int i=0, k=0; i<get_total_packages(); ++k ) {
1058                 int m = dialog->ma_gui->mixer_list->get_selection_number(0, k);
1059                 if( m == midx ) continue;
1060                 MixersAlignScanPackage *pkg = (MixersAlignScanPackage *)get_package(i++);
1061                 pkg->mixer = dialog->mixers[m];
1062         }
1063 }
1064
1065 MixersAlignScanClient::MixersAlignScanClient(MixersAlignScanFarm *farm)
1066  : LoadClient(farm)
1067 {
1068         pos = -1;
1069         len1 = 0;
1070 }
1071
1072 MixersAlignScanClient::~MixersAlignScanClient()
1073 {
1074 }
1075
1076 LoadClient* MixersAlignScanFarm::new_client()
1077 {
1078         return new MixersAlignScanClient(this);
1079 }
1080
1081 void MixersAlignScanClient::process_package(LoadPackage *package)
1082 {
1083         MixersAlignScanFarm *farm = (MixersAlignScanFarm *)server;
1084         MixersAlign *dialog = farm->dialog;
1085         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1086         if( dialog->failed ) return;
1087         pkg = (MixersAlignScanPackage *)package;
1088         MixersAlignMixer *mix = pkg->mixer;
1089
1090         EDL *edl = dialog->mixer_audio_clip(mix->mixer);
1091         MixersAlignARender audio(dialog->mwindow, edl);
1092         double start = edl->skip_silence(0);
1093         int channels = edl->get_audio_channels();
1094         int64_t sample_rate = edl->get_sample_rate();
1095         int64_t cur_pos = start * sample_rate;
1096         int64_t end_pos = edl->get_audio_samples();
1097         int len = dialog->sample_len, len2 = len/2;
1098         cur_pos &= ~(len2-1);
1099         if( cur_pos ) dialog->update_progress(cur_pos);
1100
1101         int ret = 0;
1102         Samples *samples[MAX_CHANNELS];
1103         for( int i=0; i<MAX_CHANNELS; ++i )
1104                 samples[i] = i<channels ? new Samples(len2) : 0;
1105         int cpus = bmin(dialog->mwindow->preferences->processors, channels);
1106         MixersAlignTarget targ(channels, cpus, this, samples, len2);
1107
1108         while( !ret && !dialog->failed && cur_pos < end_pos ) {
1109                 pos = cur_pos;
1110                 int64_t nxt_pos = pos + len2;
1111                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1112                 len1 = nxt_pos - cur_pos;
1113                 ret = audio.render(samples, len1, pos);
1114                 if( ret ) break;
1115                 targ.process_packages();
1116                 dialog->update_progress(len1);
1117                 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1118                 cur_pos = nxt_pos;
1119         }
1120
1121         if( !ret && !dialog->failed ) {
1122                 int idx = -1;
1123                 double sd2 = 0;
1124                 MixersAlignMixer *mix = pkg->mixer;
1125                 MixersAlignTargetPackage *best_pkg = 0;
1126                 for( int i=0,n=targ.get_total_packages(); i<n; ++i ) {
1127                         MixersAlignTargetPackage *targ_pkg =
1128                                 (MixersAlignTargetPackage *) targ.get_package(i);
1129                         if( sd2 >= targ_pkg->sd2 ) continue;
1130                         sd2 = targ_pkg->sd2;
1131                         int k = dialog->atrack_of(mix, i);
1132                         if( k < 0 ) continue;
1133                         MixersAlignATrack *atrk = dialog->atracks[k];
1134                         atrk->mi = targ_pkg->pos;
1135                         atrk->ss = targ_pkg->ss;
1136                         idx = k;  best_pkg = targ_pkg;
1137                 }
1138                 if( idx >= 0 ) {
1139                         mix->br = new double[len];
1140                         mix->bi = new double[len];
1141                         double *br = mix->br;
1142                         double *bp = best_pkg->best;
1143                         int i = 0;
1144                         while( i < len2 ) br[i++] = *bp++;
1145                         while( i < len ) br[i++] = 0;
1146                         FFT fft;
1147                         fft.do_fft(len, 0, mix->br, 0, mix->br, mix->bi);
1148                         mix->aidx = idx;
1149                 }
1150         }
1151
1152         if( ret && !dialog->failed ) {
1153                 eprintf("Audio render failed:\n%s", edl->path);
1154                 dialog->failed = 1;
1155         }
1156
1157         for( int i=channels; --i>=0; ) delete samples[i];
1158         edl->remove_user();
1159 }
1160
1161 // scan mixer channels for best target
1162 MixersAlignTarget::MixersAlignTarget(int n, int cpus,
1163                 MixersAlignScanClient *scan, Samples **samples, int len)
1164  : LoadServer(n, cpus)
1165 {
1166         this->scan = scan;
1167         this->samples = samples;
1168         this->len = len;
1169 }
1170 MixersAlignTarget::~MixersAlignTarget()
1171 {
1172 }
1173
1174 MixersAlignTargetClient::MixersAlignTargetClient()
1175  : LoadClient()
1176 {
1177 }
1178 MixersAlignTargetClient::~MixersAlignTargetClient()
1179 {
1180 }
1181
1182 LoadClient* MixersAlignTarget::new_client()
1183 {
1184         return new MixersAlignTargetClient();
1185 }
1186
1187 MixersAlignTargetPackage::MixersAlignTargetPackage(MixersAlignTarget *targ)
1188 {
1189         ss = 0;  sd2 = 0;
1190         pos = -1;
1191         best = new double[targ->len];
1192 }
1193 MixersAlignTargetPackage::~MixersAlignTargetPackage()
1194 {
1195         delete [] best;
1196 }
1197
1198 LoadPackage* MixersAlignTarget::new_package()
1199 {
1200         return new MixersAlignTargetPackage(this);
1201 }
1202
1203 void MixersAlignTarget::init_packages()
1204 {
1205 }
1206
1207 void MixersAlignTargetClient::process_package(LoadPackage *package)
1208 {
1209         MixersAlignTarget *targ = (MixersAlignTarget *)server;
1210         MixersAlignScanClient *scan = targ->scan;
1211         MixersAlignScanFarm *farm = (MixersAlignScanFarm *)scan->server;
1212         MixersAlign *dialog = farm->dialog;
1213         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1214         if( dialog->failed ) return;
1215         pkg = (MixersAlignTargetPackage *)package;
1216
1217         int ch = get_package_number();
1218         double *data = targ->samples[ch]->get_data();
1219         int len1 = scan->len1;
1220 // computes sum(s**2), sum(d2**2) d2=discrete 2nd deriv
1221 // d0=s[i+0]-s[i+1], d1=s[i+1]-s[i+2], d2=d0-d1
1222 // d = s[i+0] - 2*s[i+1] + s[i+2]
1223         double ss = 0, sd2 = 0;
1224         double a = 0, b = 0, c = 0;
1225         for( int i=0; i<len1; ++i ) {
1226                 a = b;  b = c;  c = data[i];
1227                 double d = a - 2*b + c;
1228                 ss += c*c;  sd2 += d*d;
1229         }
1230 //best is highest sd2
1231         if( pkg->sd2 < sd2 ) {
1232                 pkg->sd2 = sd2;
1233                 pkg->ss = ss;
1234                 pkg->pos = scan->pos;
1235 //printf("targ %s:%d at %jd,ss=%f sd2=%f\n",
1236 //  scan->pkg->mixer->mixer->title, ch, scan->pos, ss, sd2);
1237                 double *best = pkg->best;
1238                 int i = 0, len = targ->len;
1239                 while( i < len1 ) best[i++] = *data++;
1240                 while( i < len ) best[i++] = 0;
1241         }
1242 }
1243
1244 void MixersAlign::scan_master(Track *track)
1245 {
1246         EDL *edl = mixer_master_clip(track);
1247         MixersAlignARender audio(mwindow, edl);
1248
1249         int channels = edl->get_audio_channels();
1250         int64_t sample_rate = edl->get_sample_rate();
1251         int64_t audio_samples = edl->get_audio_samples();
1252         int64_t cur_pos = audio_start * sample_rate;
1253         int64_t end_pos = audio_end * sample_rate;
1254         if( end_pos > audio_samples ) end_pos = audio_samples;
1255         if( cur_pos >= end_pos ) {
1256                 eprintf(_("scan master track empty"));
1257                 failed = 1;
1258                 return;
1259         }
1260         int len = sample_len, len2 = len/2;
1261         double *audio_r = new double[len];
1262         double *audio_i = new double[len];
1263         Samples *samples[2][MAX_CHANNELS];
1264         for( int k=0; k<2; ++k ) {
1265                 for( int i=0; i<MAX_CHANNELS; ++i )
1266                         samples[k][i] = i<channels ? new Samples(len2) : 0;
1267         }
1268
1269         int m = 0;
1270         for( int i=0,n=mixers.size(); i<n; ++i )
1271                 if( mixers[i]->br ) ++m;
1272         int cpus = bmin(mwindow->preferences->processors, m);
1273         MixersAlignMatchRevFarm farm(m, cpus, this, audio_r, audio_i, len);
1274
1275         FFT fft;
1276         Timer timer;
1277         start_progress(end_pos - cur_pos);
1278
1279         int k = 0;
1280         int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1281         if( nxt_pos > end_pos ) nxt_pos = end_pos;
1282         int len1 = nxt_pos - pos;
1283         int ret = audio.render(samples[k], len1, pos);
1284         while( !ret && !failed && cur_pos < end_pos ) {
1285                 pos = cur_pos;
1286                 cur_pos = nxt_pos;  nxt_pos += len2;
1287                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1288                 len1 = nxt_pos - cur_pos;
1289                 ret = audio.render(samples[1-k], len1, cur_pos);
1290                 if( ret ) break;
1291                 update_progress(len2);
1292                 int i = 0;
1293                 double *lp = samples[k][0]->get_data();
1294                 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1295                 double *np = samples[k=1-k][0]->get_data();
1296                 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1297                 while( i < len ) audio_r[i++] = 0;
1298                 fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1299                 farm.pos = pos;
1300                 farm.process_packages();
1301                 if( progress->is_cancelled() ) failed = -1;
1302         }
1303
1304         if( ret && !failed ) {
1305                 eprintf("Audio render failed:\n%s", edl->path);
1306                 failed = 1;
1307         }
1308
1309         char text[BCSTRLEN];
1310         double secs = timer.get_difference()/1000.;
1311         sprintf(text, _("Match mixer done: %0.3f secs"), secs);
1312         stop_progress(text);
1313
1314         delete [] audio_r;
1315         delete [] audio_i;
1316         for( int k=0; k<2; ++k ) {
1317                 for( int i=channels; --i>=0; )
1318                         delete samples[k][i];
1319         }
1320         edl->remove_user();
1321 }
1322
1323 MixersAlignMatchFwdPackage::MixersAlignMatchFwdPackage()
1324 {
1325         mixer = 0;
1326 }
1327
1328 MixersAlignMatchFwdPackage::~MixersAlignMatchFwdPackage()
1329 {
1330 }
1331
1332 MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm(MixersAlign *dialog, int n)
1333  : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
1334 {
1335         this->dialog = dialog;
1336         dialog->farming->lock("MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm");
1337 }
1338 MixersAlignMatchFwdFarm::~MixersAlignMatchFwdFarm()
1339 {
1340         dialog->farming->unlock();
1341 }
1342
1343 LoadPackage* MixersAlignMatchFwdFarm::new_package()
1344 {
1345         return new MixersAlignMatchFwdPackage();
1346 }
1347
1348 void MixersAlignMatchFwdFarm::init_packages()
1349 {
1350         for( int i = 0; i < get_total_packages(); ++i ) {
1351                 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
1352                 MixersAlignMatchFwdPackage *package = (MixersAlignMatchFwdPackage *)get_package(i);
1353                 package->mixer = dialog->mixers[m];
1354         }
1355 }
1356
1357 LoadClient* MixersAlignMatchFwdFarm::new_client()
1358 {
1359         return new MixersAlignMatchFwdClient(this);
1360 }
1361
1362 MixersAlignMatchFwdClient::MixersAlignMatchFwdClient(MixersAlignMatchFwdFarm *farm)
1363  : LoadClient(farm)
1364 {
1365 }
1366
1367 MixersAlignMatchFwdClient::~MixersAlignMatchFwdClient()
1368 {
1369 }
1370
1371 void MixersAlignMatchFwdClient::process_package(LoadPackage *package)
1372 {
1373         MixersAlignMatchFwdFarm *farm = (MixersAlignMatchFwdFarm *)server;
1374         MixersAlign *dialog = farm->dialog;
1375         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1376         if( dialog->failed ) return;
1377         pkg = (MixersAlignMatchFwdPackage *)package;
1378
1379         MixersAlignMixer *amix = pkg->mixer;
1380         EDL *edl = dialog->mixer_audio_clip(amix->mixer);
1381         MixersAlignARender audio(dialog->mwindow, edl);
1382         int channels = edl->get_audio_channels();
1383         int64_t sample_rate = edl->get_sample_rate();
1384         int64_t audio_samples = edl->get_audio_samples();
1385         int64_t cur_pos = dialog->audio_start * sample_rate;
1386         int64_t end_pos = dialog->audio_end * sample_rate;
1387         if( end_pos > audio_samples ) end_pos = audio_samples;
1388         int len = dialog->master_len, len2 = len/2;
1389         double *audio_r = new double[len];
1390         double *audio_i = new double[len];
1391
1392         Samples *samples[2][MAX_CHANNELS];
1393         for( int k=0; k<2; ++k ) {
1394                 for( int i=0; i<MAX_CHANNELS; ++i )
1395                         samples[k][i] = i<channels ? new Samples(len2) : 0;
1396         }
1397
1398         FFT fft;
1399         int k = 0;
1400         int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1401         if( nxt_pos > end_pos ) nxt_pos = end_pos;
1402         int len1 = nxt_pos - pos;
1403         int ret = audio.render(samples[k], len1, pos);
1404         while( !ret && !dialog->failed && cur_pos < end_pos ) {
1405                 dialog->update_progress(len1);
1406                 pos = cur_pos;
1407                 cur_pos = nxt_pos;
1408                 nxt_pos += len2;
1409                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1410                 len1 = nxt_pos - cur_pos;
1411                 ret = audio.render(samples[1-k], len1, cur_pos);
1412                 if( ret ) break;
1413                 Track *track = edl->tracks->first;
1414                 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
1415                         int id = track->mixer_id, atk = dialog->atracks.size();
1416                         while( --atk >= 0 && id != dialog->atracks[atk]->track->mixer_id );
1417                         if( atk < 0 ) continue;
1418                         int i = 0;
1419                         double *lp = samples[k][ch]->get_data();
1420                         for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1421                         double *np = samples[1-k][ch]->get_data();
1422                         for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1423                         while( i < len ) audio_r[i++] = 0;
1424                         fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1425                         conj_product(len, audio_r, audio_i, audio_r, audio_i,
1426                                         dialog->master_r, dialog->master_i);
1427                         fft.do_fft(len, 1, audio_r, audio_i);
1428                         double mx = 0;  int64_t mi = -1;
1429                         for( int i=0; i<len2; ++i ) {
1430                                 double v = fabs(audio_r[i]);
1431                                 if( mx < v ) { mx = v;  mi = i + pos; }
1432                         }
1433                         mx /= dialog->master_ss;
1434                         MixersAlignATrack *atrack= dialog->atracks[atk];
1435                         if( atrack->mx < mx ) {
1436                                 atrack->mx = mx;
1437                                 atrack->mi = mi;
1438                         }
1439                 }
1440                 k = 1-k;
1441                 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1442         }
1443         if( ret && !dialog->failed ) {
1444                 eprintf("Audio render failed:\n%s", edl->path);
1445                 dialog->failed = 1;
1446         }
1447         delete [] audio_r;
1448         delete [] audio_i;
1449         for( int k=0; k<2; ++k ) {
1450                 for( int i=channels; --i>=0; )
1451                         delete samples[k][i];
1452         }
1453         edl->remove_user();
1454 }
1455
1456
1457 MixersAlignMatchRevFarm::MixersAlignMatchRevFarm(int n, int cpus,
1458                 MixersAlign *dialog, double *ar, double *ai, int len)
1459  : LoadServer(n, cpus)
1460 {
1461         this->dialog = dialog;
1462         this->ar = ar;
1463         this->ai = ai;
1464         this->len = len;
1465         mixer_lock = new Mutex("MixersAlignMatchRevFarm::mixer_lock");
1466         pos = -1;
1467 }
1468 MixersAlignMatchRevFarm::~MixersAlignMatchRevFarm()
1469 {
1470         delete mixer_lock;
1471 }
1472
1473 MixersAlignMatchRevPackage::MixersAlignMatchRevPackage()
1474 {
1475         mix = 0;
1476 }
1477 MixersAlignMatchRevPackage::~MixersAlignMatchRevPackage()
1478 {
1479 }
1480
1481 void MixersAlignMatchRevFarm::init_packages()
1482 {
1483         for( int i=0,m=0,n=dialog->mixers.size(); m<n; ++m ) {
1484                 if( !dialog->mixers[m]->br ) continue;
1485                 MixersAlignMatchRevPackage *pkg = (MixersAlignMatchRevPackage *)get_package(i++);
1486                 pkg->mix = dialog->mixers[m];
1487         }
1488 }
1489
1490 LoadClient *MixersAlignMatchRevFarm::new_client()
1491 {
1492         return new MixersAlignMatchRevClient(this);
1493 }
1494 LoadPackage *MixersAlignMatchRevFarm::new_package()
1495 {
1496         return new MixersAlignMatchRevPackage();
1497 }
1498
1499 void MixersAlignMatchRevClient::process_package(LoadPackage *package)
1500 {
1501         MixersAlignMatchRevFarm *farm = (MixersAlignMatchRevFarm *)server;
1502         MixersAlign *dialog = farm->dialog;
1503         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1504         if( dialog->failed ) return;
1505         pkg = (MixersAlignMatchRevPackage *)package;
1506         MixersAlignMixer *mix = pkg->mix;
1507         if( mix->aidx < 0 ) return;
1508         int64_t ss = dialog->atracks[mix->aidx]->ss;
1509
1510         conj_product(farm->len, re, im, farm->ar, farm->ai, mix->br, mix->bi);
1511         FFT fft;
1512         fft.do_fft(farm->len, 1, re, im);
1513         double mx = 0;  int64_t mi = -1;
1514         for( int i=0,n=farm->len/2; i<n; ++i ) {
1515                 double r = fabs(re[i]) / ss;
1516                 if( mx < r ) {
1517                         mx = r;
1518                         mi = i + farm->pos;
1519                 }
1520         }
1521         farm->mixer_lock->lock("MixersAlignMatchRevFarm::process_package");
1522         if( mix->mx < mx ) {
1523                 mix->mx = mx;
1524                 mix->mi = mi;
1525 //printf("best %d: %f at %jd\n", get_package_number(), mx, mi);
1526         }
1527         farm->mixer_lock->unlock();
1528 }
1529
1530 MixersAlignMatchRevClient::MixersAlignMatchRevClient(MixersAlignMatchRevFarm *farm)
1531 {
1532         re = new double[farm->len];
1533         im = new double[farm->len];
1534 }
1535 MixersAlignMatchRevClient::~MixersAlignMatchRevClient()
1536 {
1537         delete [] re;
1538         delete [] im;
1539 }
1540
1541 void MixersAlign::start_progress(int64_t total_len)
1542 {
1543         total_rendered = 0;
1544         mwindow->gui->lock_window("MixersAlign::start_progress");
1545         progress = mwindow->mainprogress->
1546                 start_progress(_("match mixer audio"), total_len);
1547         mwindow->gui->unlock_window();
1548 }
1549 void MixersAlign::stop_progress(const char *msg)
1550 {
1551         mwindow->gui->lock_window("MixersAlign::stop_progress");
1552         progress->update(0);
1553         mwindow->mainprogress->end_progress(progress);
1554         progress = 0;
1555         if( msg ) mwindow->gui->show_message(msg);
1556         mwindow->gui->unlock_window();
1557 }
1558
1559 void MixersAlign::reset_targets()
1560 {
1561         for( int m=0,n=mixers.size(); m<n; ++m ) {
1562                 MixersAlignMixer *mix = mixers[m];
1563                 delete mix->br;  mix->br = 0;
1564                 delete mix->bi;  mix->bi = 0;
1565                 mix->mi = 0;
1566                 mix->aidx = -1;
1567         }
1568         for( int i=0,n=atracks.size(); i<n; ++i ) {
1569                 MixersAlignATrack *atrk = atracks[i];
1570                 atrk->nudge = 0; atrk->ss = 0;
1571                 atrk->mx = 0;    atrk->mi = -1;
1572         }
1573 }
1574
1575 void MixersAlign::scan_targets()
1576 {
1577         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1578         int midx = mmixer_of(idx);
1579         int64_t total_len = mixer_tracks_total(midx);
1580         start_progress(total_len);
1581         int m = mixers.size();
1582         if( midx >= 0 ) --m;
1583         int cpus = bmin(mwindow->preferences->processors, m);
1584         MixersAlignScanFarm scan(this, cpus, m);
1585         scan.process_packages();
1586         stop_progress(0);
1587 }
1588
1589 void MixersAlign::update_progress(int64_t len)
1590 {
1591         total_lock->lock();
1592         total_rendered += len;
1593         total_lock->unlock();
1594         progress->update(total_rendered);
1595 }
1596
1597 static inline uint64_t high_bit_mask(uint64_t n)
1598 {
1599         n |= n >> 1;   n |= n >> 2;
1600         n |= n >> 4;   n |= n >> 8;
1601         n |= n >> 16;  n |= n >> 32;
1602         return n;
1603 }
1604
1605 void MixersAlign::load_master_audio(Track *track)
1606 {
1607         EDL *edl = mixer_master_clip(track);
1608         MixersAlignARender audio(mwindow, edl);
1609         int channels = edl->get_audio_channels();
1610         int64_t sample_rate = edl->get_sample_rate();
1611         int64_t audio_samples = edl->get_audio_samples();
1612         int64_t cur_pos = master_start * sample_rate;
1613         int64_t end_pos = master_end * sample_rate;
1614         if( end_pos > audio_samples ) end_pos = audio_samples;
1615         if( cur_pos >= end_pos ) {
1616                 eprintf(_("master audio track empty"));
1617                 failed = 1;
1618                 return;
1619         }
1620         int64_t audio_len = end_pos - cur_pos;
1621         if( audio_len > sample_rate * 60 ) {
1622                 eprintf(_("master audio track length > 60 seconds"));
1623                 failed = 1;
1624                 return;
1625         }
1626         int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
1627         if( master_len != fft_len ) {
1628                 master_len = fft_len;
1629                 delete [] master_r;  master_r = new double[master_len];
1630                 delete [] master_i;  master_i = new double[master_len];
1631         }
1632
1633         Samples *samples[MAX_CHANNELS];
1634         for( int i=0; i<MAX_CHANNELS; ++i )
1635                 samples[i] = i<channels ? new Samples(audio_len) : 0;
1636         int ret = audio.render(samples, audio_len, cur_pos);
1637         if( ret ) failed = 1;
1638         edl->remove_user();
1639         if( !failed ) {
1640                 double *dp = samples[0]->get_data();
1641                 int i = 0;  double ss = 0;
1642                 for( ; i<audio_len; ++i ) { ss += *dp * *dp;  master_r[i] = *dp++; }
1643                 master_ss = ss;
1644                 for( ; i<fft_len; ++i ) master_r[i] = 0;
1645                 for( int i=channels; --i>=0; ) delete samples[i];
1646         }
1647         do_fft(fft_len, 0, master_r, 0, master_r, master_i);
1648 }
1649
1650 void MixersAlign::scan_mixer_audio()
1651 {
1652         for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
1653         int n = 0;
1654         while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
1655         if( !n ) {
1656                 eprintf(_("no mixers selected"));
1657                 return;
1658         }
1659
1660         Timer timer;
1661         MixersAlignMatchFwdFarm farm(this, n);
1662         int64_t total_len = mixer_tracks_total(-1);
1663         start_progress(total_len);
1664
1665         farm.process_packages();
1666         if( progress->is_cancelled() ) failed = -1;
1667
1668         char text[BCSTRLEN];
1669         double secs = timer.get_difference()/1000.;
1670         sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1671         stop_progress(text);
1672 }
1673
1674 void MixersAlign::match_fwd()
1675 {
1676         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1677         if( idx < 0 ) {
1678                 eprintf(_("selection (master) not set"));
1679                 return;
1680         }
1681         master_start = mwindow->edl->local_session->get_inpoint();
1682         if( master_start < 0 ) {
1683                 eprintf(_("in point selection (master start) must be set"));
1684                 return;
1685         }
1686         master_end = mwindow->edl->local_session->get_outpoint();
1687         if( master_end < 0 ) {
1688                 eprintf(_("out point selection (master end) must be set"));
1689                 return;
1690         }
1691         if( master_start >= master_end ) {
1692                 eprintf(_("in/out point selection (master start/end) invalid"));
1693                 return;
1694         }
1695         audio_start = mwindow->edl->local_session->get_selectionstart();
1696         audio_end = mwindow->edl->local_session->get_selectionend();
1697         if( audio_start >= audio_end ) {
1698                 eprintf(_("selection (audio start/end) invalid"));
1699                 return;
1700         }
1701
1702         failed = 0;
1703         if( !failed )
1704                 load_master_audio(mtracks[idx]->track);
1705         if( !failed )
1706                 scan_mixer_audio();
1707         if( !failed )
1708                 update_fwd();
1709         if( failed < 0 ) {
1710                 mwindow->gui->lock_window("MixersAlign::update_match_fwd");
1711                 mwindow->gui->show_message(_("mixer selection match canceled"));
1712                 mwindow->gui->unlock_window();
1713         }
1714         else if( failed > 0 )
1715                 eprintf(_("Error in match render."));
1716 }
1717
1718 void MixersAlign::match_rev()
1719 {
1720         int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1721         if( midx < 0 ) {
1722                 eprintf(_("selection (master) not set"));
1723                 return;
1724         }
1725         Track *track = mtracks[midx]->track;
1726         audio_start = mwindow->edl->local_session->get_selectionstart();
1727         audio_end = mwindow->edl->local_session->get_selectionend();
1728         if( audio_start >= audio_end ) {
1729                 eprintf(_("selection (audio start/end) invalid"));
1730                 return;
1731         }
1732
1733         reset_targets();
1734         failed = 0;
1735
1736         if( !failed )
1737                 scan_targets();
1738         if( !failed )
1739                 scan_master(track);
1740         if( !failed )
1741                 update_rev();
1742
1743         if( failed < 0 ) {
1744                 mwindow->gui->lock_window("MixersAlign::update_match_rev");
1745                 mwindow->gui->show_message(_("mixer selection match canceled"));
1746                 mwindow->gui->unlock_window();
1747         }
1748         else if( failed > 0 )
1749                 eprintf(_("Error in match render."));
1750 }
1751
1752 void MixersAlign::update_fwd()
1753 {
1754         double sample_rate = mwindow->edl->get_sample_rate();
1755         int64_t mi = master_start * sample_rate;
1756 // mixer best matches
1757         for( int m=0; m<mixers.size(); ++m ) {
1758                 MixersAlignMixer *mix = mixers[m];
1759                 Mixer *mixer = mix->mixer;
1760                 mix->mx = 0;  mix->mi = -1;  mix->aidx = -1;
1761                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1762                         int id = mixer->mixer_ids[i], k = atracks.size();
1763                         while( --k >= 0 && atracks[k]->track->mixer_id != id );
1764                         if( k < 0 ) continue;
1765                         MixersAlignATrack *atrk = atracks[k];
1766                         atrk->nudge = atrk->mi < 0 ? -1 :
1767                                 (mi - atrk->mi) / sample_rate;
1768                         if( mix->mx >= atrk->mx ) continue;
1769                         mix->aidx = k;
1770                         mix->mx = atrk->mx;
1771                         mix->mi = atrk->mi;
1772                         mix->nudge = atrk->nudge;
1773                 }
1774         }
1775         update();
1776 }
1777
1778 void MixersAlign::update_rev()
1779 {
1780         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1781         int midx = mmixer_of(idx);
1782         double sample_rate = mwindow->edl->get_sample_rate();
1783         for( int m=0,n=mixers.size(); m<n; ++m ) {
1784                 if( m == midx ) continue;
1785                 if( !ma_gui->mixer_list->is_selected(m) ) continue;
1786                 MixersAlignMixer *mix = mixers[m];
1787                 if( mix->aidx < 0 ) continue;
1788                 MixersAlignATrack *atrk = atracks[mix->aidx];
1789                 mix->nudge = atrk->mi < 0 ? -1 :
1790                         (mix->mi - atrk->mi) / sample_rate;
1791                 atrk->mx = mix->mx;
1792         }
1793         update();
1794 }
1795
1796 void MixersAlign::update()
1797 {
1798         ma_gui->lock_window("MixersAlign::update");
1799         ma_gui->mixer_list->load_list();
1800         ma_gui->mixer_list->set_all_selected(1);
1801         ma_gui->mixer_list->update();
1802
1803         ma_gui->atrack_list->load_list();
1804         for( int m=0; m<mixers.size(); ++m ) {
1805                 int aidx = mixers[m]->aidx;
1806                 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1807         }
1808         ma_gui->atrack_list->update();
1809         ma_gui->unlock_window();
1810 }
1811