6e9abcc6260340dcace8b27e35e952b370f5ecaa
[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] = xS(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] = xS(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] = xS(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, xS(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, xS(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, xS(880), yS(380), xS(880), yS(380), 1)
522 {
523         this->dialog = dialog;
524 // *** CONTEXT_HELP ***
525         context_help_set_keyword("Video sync using Waveforms");
526 }
527 MixersAlignWindow::~MixersAlignWindow()
528 {
529 }
530
531 void MixersAlignWindow::create_objects()
532 {
533         int xs10 = xS(10), xs20 = xS(20);
534         int ys10 = yS(10), ys20 = yS(20);
535         int x = xs10, y = ys10, w4 = (get_w()-x-xs10)/4, lw = w4 + xs20;
536         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
537         mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
538         add_subwindow(mixer_title);
539         mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
540         add_subwindow(mtrack_title);
541         atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
542         add_subwindow(atrack_title);
543         y += mixer_title->get_h() + ys10;
544         int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - yS(32);
545         int lh = y2 - y1;
546         mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-xs20, lh);
547         add_subwindow(mixer_list);
548         mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-xs20, lh);
549         add_subwindow(mtrack_list);
550         atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-xs20, lh);
551         add_subwindow(atrack_list);
552         int xr = x2-xs10 - MixersAlignReset::calculate_width(this);
553         y1 = y2+ys20;
554         add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y1));
555         add_subwindow(match = new MixersAlignMatch(this, dialog, x2+xs10, y1));
556         int xa = x3-xs10 - MixersAlignNudgeTracks::calculate_width(this);
557         add_subwindow(nudge_tracks = new MixersAlignNudgeTracks(this, dialog, xa, y1));
558         y2 = y1 + nudge_tracks->get_h() + ys10;
559         add_subwindow(match_all = new MixersAlignMatchAll(this, dialog, xr, y2));
560         add_subwindow(nudge_selected = new MixersAlignNudgeSelected(this, dialog, xa, y2));
561         int xu = x3+xs10;
562         add_subwindow(check_point = new MixersAlignCheckPoint(this, dialog, xu, y1));
563         add_subwindow(undo = new MixersAlignUndo(this, dialog, xu, y2));
564         undo->create_objects();
565
566         add_subwindow(new BC_OKButton(this));
567         add_subwindow(new BC_CancelButton(this));
568 }
569
570 int MixersAlignWindow::resize_event(int w, int h)
571 {
572         int xs10 = xS(10), xs20 = xS(20);
573         int ys10 = yS(10), ys20 = yS(20);
574         int x = xs10, y = ys10, w4 = (w-x-xs10)/4, lw = w4 + xs20;
575         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
576         mixer_title->reposition_window(x1, y);
577         mtrack_title->reposition_window(x2, y);
578         atrack_title->reposition_window(x3, y);
579         y += mixer_title->get_h() + ys10;
580         int y1 = y, y2 = h - BC_OKButton::calculate_h() - yS(32);
581         int lh = y2 - y1;
582         mixer_list->reposition_window(x1, y, x2-x1-xs20, lh);
583         mtrack_list->reposition_window(x2, y, x3-x2-xs20, lh);
584         atrack_list->reposition_window(x3, y, x4-x3-xs20, lh);
585         int xr = x2-xs10 - MixersAlignReset::calculate_width(this);
586         y1 = y2+ys20;
587         reset->reposition_window(xr, y1);
588         match->reposition_window(x2+xs10, y1);
589         int xa = x3-xs10 - MixersAlignNudgeTracks::calculate_width(this);
590         nudge_tracks->reposition_window(xa, y1);
591         y2 = y1 + nudge_tracks->get_h() + ys10;
592         match_all->reposition_window(xr, y2);
593         nudge_selected->reposition_window(xa, y2);
594         int xu = x3+xs10;
595         check_point->reposition_window(xu, y1);
596         undo->reposition_window(xu, y2);
597         return 0;
598 }
599
600 void MixersAlignWindow::load_lists()
601 {
602         mixer_list->load_list();
603         mtrack_list->load_list();
604         atrack_list->load_list();
605 }
606
607 void MixersAlignWindow::default_selection()
608 {
609 // mixers selects all mixers
610         mixer_list->set_all_selected(1);
611 // master selects first mixer audio track
612         for( int i=0; i<dialog->mtracks.size(); ++i ) {
613                 if( dialog->mmixer_of(i) >= 0 ) {
614                         mtrack_list->set_selected(i, 1);
615                         break;
616                 }
617         }
618 // audio selects all mixer audio tracks
619         for( int i=0; i<dialog->atracks.size(); ++i ) {
620                 if( dialog->amixer_of(i) >= 0 )
621                         atrack_list->set_selected(i, 1);
622         }
623         update_gui();
624 }
625
626 void MixersAlignWindow::update_gui()
627 {
628         mixer_list->update();
629         mtrack_list->update();
630         atrack_list->update();
631 }
632
633
634 MixersAlign::MixersAlign(MWindow *mwindow)
635 {
636         this->mwindow = mwindow;
637         this->ma_gui = 0;
638         farming = new Mutex("MixersAlign::farming");
639         total_lock = new Mutex("MixersAlign::total_lock");
640         thread = new MixersAlignThread(this);
641         total_rendered = 0;
642         master_r = 0;
643         master_i = 0;
644         master_len = 0;
645         sample_len = 0x10000;
646         progress = 0;
647         failed = 0;
648         master_start = master_end = 0;
649         audio_start = audio_end = 0;
650 }
651
652 MixersAlign::~MixersAlign()
653 {
654         failed = -1;
655         farming->lock("MixersAlign::~MixersAlign");
656         close_window();
657         delete farming;
658         delete thread;
659         delete total_lock;
660         delete progress;
661         delete [] master_r;
662         delete [] master_i;
663 }
664
665 void MixersAlign::start_dialog(int wx, int wy)
666 {
667         this->wx = wx;
668         this->wy = wy;
669         EDL *start_over = new EDL();
670         start_over->create_objects();
671         start_over->copy_all(mwindow->edl);
672         undo_edls.append(start_over);
673         start();
674 }
675
676 BC_Window *MixersAlign::new_gui()
677 {
678         load_mixers();
679         load_mtracks();
680         load_atracks();
681         ma_gui = new MixersAlignWindow(this, wx, wy);
682         ma_gui->lock_window("MixersAlign::new_gui");
683         ma_gui->create_objects();
684         ma_gui->load_lists();
685         ma_gui->default_selection();
686         ma_gui->show_window(1);
687         ma_gui->unlock_window();
688         return ma_gui;
689 }
690
691 // shift armed mixer tracks by nudge
692 void MixersAlign::nudge_tracks()
693 {
694         mwindow->gui->lock_window("MixersAlign::apply_tracks");
695         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
696         int midx = mmixer_of(idx);
697         EDL *edl = mwindow->edl;
698         
699         for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
700                 if( m == midx ) continue;  // master does not move
701                 MixersAlignMixer *mix = mixers[m];
702                 Mixer *mixer = mix->mixer;
703                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
704                         int id = mixer->mixer_ids[i];
705                         Track *track = edl->tracks->first;
706                         while( track && track->mixer_id != id ) track = track->next;
707                         if( !track ) continue;
708                         double nudge = mix->nudge;
709                         int armed = track->armed;  track->armed = 1;
710                         if( nudge < 0 ) {
711                                 track->clear(0, -nudge, 1,
712                                         edl->session->labels_follow_edits,
713                                         edl->session->plugins_follow_edits,
714                                         edl->session->autos_follow_edits, 0);
715                         }
716                         else if( nudge > 0 ) {
717                                 track->paste_silence(0, nudge,
718                                         edl->session->plugins_follow_edits,
719                                         edl->session->autos_follow_edits);
720                         }
721                         track->armed = armed;
722                 }
723         }
724         edl->optimize();
725
726         mwindow->update_gui(1);
727         mwindow->gui->unlock_window();
728         clear_mixer_nudge();
729 }
730
731 // move selected mixer edits by nudge
732 void MixersAlign::nudge_selected()
733 {
734         mwindow->gui->lock_window("MixersAlign::apply_selected");
735         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
736         int midx = mmixer_of(idx);
737         EDL *edl = mwindow->edl;
738
739         ArrayList<int> track_arms;  // ugly
740         for( Track *track=edl->tracks->first; track; track=track->next ) {
741                 track_arms.append(track->armed);
742                 track->armed = 0;
743         }
744         for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
745                 if( m == midx ) continue;  // master does not move
746                 MixersAlignMixer *mix = mixers[m];
747                 Mixer *mixer = mix->mixer;
748                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
749                         int id = mixer->mixer_ids[i];
750                         Track *track = edl->tracks->first;
751                         while( track && track->mixer_id != id ) track = track->next;
752                         if( !track ) continue;
753                         double nudge = mix->nudge;
754                         track->armed = 1;
755                         double position = 0;  Track *first_track = 0;
756                         EDL *clip = edl->selected_edits_to_clip(0, &position, &first_track);
757                         if( clip ) {
758                                 Track *clip_track = clip->tracks->first;
759                                 Track *edl_track = first_track;
760                                 while( clip_track && edl_track ) {
761                                         Edit *edit = clip_track->edits->first;
762                                         for( ; edit; edit=edit->next ) {
763                                                 double start = clip_track->from_units(edit->startproject);
764                                                 double end = clip_track->from_units(edit->startproject+edit->length);
765                                                 start += position;  end += position;
766                                                 edl_track->clear(start, end, 1,
767                                                         edl->session->labels_follow_edits,
768                                                         edl->session->plugins_follow_edits,
769                                                         edl->session->autos_follow_edits, 0);
770                                                 edl_track->paste_silence(start, end,
771                                                         edl->session->plugins_follow_edits,
772                                                         edl->session->autos_follow_edits);
773                                         }
774                                         clip_track = clip_track->next;
775                                         edl_track = edl_track->next;
776                                 }
777                                 position += nudge;
778                                 edl->paste_edits(clip, first_track, position, 1);
779                         }
780                         track->armed = 0;
781                 }
782         }
783         int i = 0;
784         for( Track *track=edl->tracks->first; track; track=track->next )
785                 track->armed = track_arms[i++];
786         edl->optimize();
787
788         mwindow->update_gui(1);
789         mwindow->gui->unlock_window();
790         clear_mixer_nudge();
791 }
792
793
794 void MixersAlign::clear_mixer_nudge()
795 {
796 // so pressing apply twice does not damage the result
797         for( int m=0; m<mixers.size(); ++m )
798                 mixers[m]->nudge = 0;
799         ma_gui->mixer_list->load_list();
800         ma_gui->lock_window("MixersAlign::clear_mixer_nudge");
801         ma_gui->mixer_list->update();
802         ma_gui->unlock_window();
803 }
804
805 void MixersAlign::check_point()
806 {
807         mwindow->gui->lock_window("MixersAlign::check_point");
808         ma_gui->undo->add_undo_item(undo_edls.size());
809         EDL *undo_edl = new EDL();
810         undo_edl->create_objects();
811         undo_edl->copy_all(mwindow->edl);
812         undo_edls.append(undo_edl);
813         mwindow->gui->unlock_window();
814 }
815
816
817 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
818  : Thread(1, 0, 0)
819 {
820         this->dialog = dialog;
821 }
822 MixersAlignThread::~MixersAlignThread()
823 {
824         join();
825 }
826
827 void MixersAlignThread::start(int fwd)
828 {
829         this->fwd = fwd;
830         MixersAlignWindow *gui = dialog->ma_gui;
831         gui->reset->disable();
832         gui->match->disable();
833         gui->match_all->disable();
834         gui->nudge_tracks->disable();
835         gui->nudge_selected->disable();
836         gui->check_point->disable();
837 //      gui->undo->disable();
838         Thread::start();
839 }
840
841 void MixersAlignThread::run()
842 {
843         if( fwd )
844                 dialog->match_fwd();
845         else
846                 dialog->match_rev();
847         MixersAlignWindow *gui = dialog->ma_gui;
848         gui->lock_window("MixersAlignThread::run");
849         gui->reset->enable();
850         gui->match->enable();
851         gui->match_all->enable();
852         gui->nudge_tracks->enable();
853         gui->nudge_selected->enable();
854         gui->check_point->enable();
855 //      gui->undo->enable();
856         gui->unlock_window();
857 }
858
859
860 void MixersAlign::handle_done_event(int result)
861 {
862         if( thread->running() ) {
863                 failed = -1;
864                 thread->join();
865         }
866         if( !result ) {
867                 mwindow->gui->lock_window("MixersAlign::handle_done_event");
868                 EDL *edl = mwindow->edl;
869                 mwindow->edl = undo_edls[0];
870                 mwindow->undo_before();
871                 mwindow->edl = edl;
872                 mwindow->undo_after(_("align mixers"), LOAD_ALL);
873                 mwindow->gui->unlock_window();
874         }
875 }
876
877 void MixersAlign::handle_close_event(int result)
878 {
879         ma_gui = 0;
880         mixers.clear();
881         mtracks.clear();
882         atracks.clear();
883         undo_edls.clear();
884 }
885
886 void MixersAlign::load_mixers()
887 {
888         mixers.clear();
889         Mixers &edl_mixers = mwindow->edl->mixers;
890         for( int i=0; i<edl_mixers.size(); ++i )
891                 mixers.append(new MixersAlignMixer(edl_mixers[i]));
892 }
893
894 void MixersAlign::load_mtracks()
895 {
896         mtracks.clear();
897         Track *track=mwindow->edl->tracks->first;
898         for( int no=0; track; ++no, track=track->next ) {
899                 if( track->data_type != TRACK_AUDIO ) continue;
900                 mtracks.append(new MixersAlignMTrack(track, no));
901         }
902 }
903
904 void MixersAlign::load_atracks()
905 {
906         atracks.clear();
907         Track *track=mwindow->edl->tracks->first;
908         for( int no=0; track; ++no, track=track->next ) {
909                 if( track->data_type != TRACK_AUDIO ) continue;
910                 if( mixer_of(track) < 0 ) continue;
911                 atracks.append(new MixersAlignATrack(track, no));
912         }
913 }
914
915
916 int MixersAlign::atrack_of(MixersAlignMixer *mix, int ch)
917 {
918         int k = -1;
919         Mixer *mixer = mix->mixer;
920         for( int i=0,n=mixer->mixer_ids.size(); i<n; ++i ) {
921                 int id = mixer->mixer_ids[i];  k = atracks.size();
922                 while( --k >= 0 && atracks[k]->track->mixer_id != id );
923                 if( k < 0 ) continue;
924                 if( --ch < 0 ) break;
925         }
926         return k;
927 }
928
929 int MixersAlign::mixer_of(Track *track, int &midx)
930 {
931         int id = track->mixer_id, k = mixers.size(), idx = -1;
932         while( idx < 0 && --k >= 0 )
933                 idx = mixers[k]->mixer->mixer_ids.number_of(id);
934         midx = idx;
935         return k;
936 }
937
938
939 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
940 {
941         EDL *edl = new EDL(mwindow->edl);
942         edl->create_objects();
943         Track *track = mwindow->edl->tracks->first;
944         for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
945                 int id = track->mixer_id;
946                 if( track->data_type != TRACK_AUDIO ) continue;
947                 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
948                 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
949                 mixer_audio->copy_settings(track);
950                 mixer_audio->play = 1;
951                 mixer_audio->edits->copy_from(track->edits);
952                 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
953                                 autos[AUTOMATION_PAN]->default_auto;
954                 pan_auto->values[ch++] = 1.0;
955         }
956         return edl;
957 }
958
959 EDL *MixersAlign::mixer_master_clip(Track *track)
960 {
961         EDL *edl = new EDL(mwindow->edl);
962         edl->create_objects();
963         Track *master_audio = edl->tracks->add_audio_track(0, 0);
964         master_audio->copy_settings(track);
965         master_audio->play = 1;
966         master_audio->edits->copy_from(track->edits);
967         PanAuto* pan_auto = (PanAuto*)master_audio->automation->
968                         autos[AUTOMATION_PAN]->default_auto;
969         pan_auto->values[0] = 1.0;
970         return edl;
971 }
972
973 int64_t MixersAlign::mixer_tracks_total(int midx)
974 {
975         int64_t total_len = 0;
976         int64_t sample_rate = mwindow->edl->get_sample_rate();
977         int m = -1;
978         for( int i=0; (m=ma_gui->mixer_list->get_selection_number(0, i))>=0 ; ++i ) {
979                 if( m == midx ) continue;
980                 Mixer *mixer = mixers[m]->mixer;
981                 double render_end = 0;
982                 Track *track = mwindow->edl->tracks->first;
983                 for( ; track; track=track->next ) {
984                         int id = track->mixer_id;
985                         if( track->data_type != TRACK_AUDIO ) continue;
986                         if( mixer->mixer_ids.number_of(id) < 0 ) continue;
987                         double track_end = track->get_length();
988                         if( render_end < track_end ) render_end = track_end;
989                 }
990                 if( render_end > audio_end ) render_end = audio_end;
991                 double len = render_end - audio_start;
992                 if( len > 0 ) total_len += len * sample_rate;
993         }
994         return total_len;
995 }
996
997 void MixersAlign::apply_undo(int no)
998 {
999         if( thread->running() ) {
1000                 failed = -1;
1001                 thread->join();
1002         }
1003         mwindow->gui->lock_window("MixersAlignUndo::handle_event");
1004         EDL *undo_edl = undo_edls[no];
1005         mwindow->edl->copy_all(undo_edl);
1006         mwindow->update_gui(1);
1007         mwindow->gui->unlock_window();
1008         ma_gui->reset->handle_event();
1009 }
1010
1011 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
1012  : RenderEngine(0, mwindow->preferences, 0, 0)
1013 {
1014         TransportCommand command;
1015         command.command = NORMAL_FWD;
1016         command.get_edl()->copy_all(edl);
1017         command.change_type = CHANGE_ALL;
1018         command.realtime = 0;
1019         set_vcache(mwindow->video_cache);
1020         set_acache(mwindow->audio_cache);
1021         arm_command(&command);
1022 }
1023
1024 MixersAlignARender::~MixersAlignARender()
1025 {
1026 }
1027
1028 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
1029 {
1030         return arender ? arender->process_buffer(samples, len, pos) : -1;
1031 }
1032
1033 // scan mixer tracks for best target
1034 MixersAlignScanFarm::MixersAlignScanFarm(MixersAlign *dialog, int cpus, int n)
1035  : LoadServer(cpus, n)
1036 {
1037         dialog->farming->lock("MixersAlignScanFarm::MixersAlignScanFarm");
1038         this->dialog = dialog;
1039         this->len = len;
1040 }
1041 MixersAlignScanFarm::~MixersAlignScanFarm()
1042 {
1043         dialog->farming->unlock();
1044 }
1045
1046 MixersAlignScanPackage::MixersAlignScanPackage(MixersAlignScanFarm *farm)
1047 {
1048         mixer = 0;
1049 }
1050 MixersAlignScanPackage::~MixersAlignScanPackage()
1051 {
1052 }
1053
1054 LoadPackage* MixersAlignScanFarm::new_package()
1055 {
1056         return new MixersAlignScanPackage(this);
1057 }
1058
1059 void MixersAlignScanFarm::init_packages()
1060 {
1061         int idx = dialog->ma_gui->mtrack_list->get_selection_number(0, 0);
1062         int midx = dialog->mmixer_of(idx);
1063         for( int i=0, k=0; i<get_total_packages(); ++k ) {
1064                 int m = dialog->ma_gui->mixer_list->get_selection_number(0, k);
1065                 if( m == midx ) continue;
1066                 MixersAlignScanPackage *pkg = (MixersAlignScanPackage *)get_package(i++);
1067                 pkg->mixer = dialog->mixers[m];
1068         }
1069 }
1070
1071 MixersAlignScanClient::MixersAlignScanClient(MixersAlignScanFarm *farm)
1072  : LoadClient(farm)
1073 {
1074         pos = -1;
1075         len1 = 0;
1076 }
1077
1078 MixersAlignScanClient::~MixersAlignScanClient()
1079 {
1080 }
1081
1082 LoadClient* MixersAlignScanFarm::new_client()
1083 {
1084         return new MixersAlignScanClient(this);
1085 }
1086
1087 void MixersAlignScanClient::process_package(LoadPackage *package)
1088 {
1089         MixersAlignScanFarm *farm = (MixersAlignScanFarm *)server;
1090         MixersAlign *dialog = farm->dialog;
1091         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1092         if( dialog->failed ) return;
1093         pkg = (MixersAlignScanPackage *)package;
1094         MixersAlignMixer *mix = pkg->mixer;
1095
1096         EDL *edl = dialog->mixer_audio_clip(mix->mixer);
1097         MixersAlignARender audio(dialog->mwindow, edl);
1098         double start = edl->skip_silence(0);
1099         int channels = edl->get_audio_channels();
1100         int64_t sample_rate = edl->get_sample_rate();
1101         int64_t cur_pos = start * sample_rate;
1102         int64_t end_pos = edl->get_audio_samples();
1103         int len = dialog->sample_len, len2 = len/2;
1104         cur_pos &= ~(len2-1);
1105         if( cur_pos ) dialog->update_progress(cur_pos);
1106
1107         int ret = 0;
1108         Samples *samples[MAX_CHANNELS];
1109         for( int i=0; i<MAX_CHANNELS; ++i )
1110                 samples[i] = i<channels ? new Samples(len2) : 0;
1111         int cpus = bmin(dialog->mwindow->preferences->processors, channels);
1112         MixersAlignTarget targ(channels, cpus, this, samples, len2);
1113
1114         while( !ret && !dialog->failed && cur_pos < end_pos ) {
1115                 pos = cur_pos;
1116                 int64_t nxt_pos = pos + len2;
1117                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1118                 len1 = nxt_pos - cur_pos;
1119                 ret = audio.render(samples, len1, pos);
1120                 if( ret ) break;
1121                 targ.process_packages();
1122                 dialog->update_progress(len1);
1123                 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1124                 cur_pos = nxt_pos;
1125         }
1126
1127         if( !ret && !dialog->failed ) {
1128                 int idx = -1;
1129                 double sd2 = 0;
1130                 MixersAlignMixer *mix = pkg->mixer;
1131                 MixersAlignTargetPackage *best_pkg = 0;
1132                 for( int i=0,n=targ.get_total_packages(); i<n; ++i ) {
1133                         MixersAlignTargetPackage *targ_pkg =
1134                                 (MixersAlignTargetPackage *) targ.get_package(i);
1135                         if( sd2 >= targ_pkg->sd2 ) continue;
1136                         sd2 = targ_pkg->sd2;
1137                         int k = dialog->atrack_of(mix, i);
1138                         if( k < 0 ) continue;
1139                         MixersAlignATrack *atrk = dialog->atracks[k];
1140                         atrk->mi = targ_pkg->pos;
1141                         atrk->ss = targ_pkg->ss;
1142                         idx = k;  best_pkg = targ_pkg;
1143                 }
1144                 if( idx >= 0 ) {
1145                         mix->br = new double[len];
1146                         mix->bi = new double[len];
1147                         double *br = mix->br;
1148                         double *bp = best_pkg->best;
1149                         int i = 0;
1150                         while( i < len2 ) br[i++] = *bp++;
1151                         while( i < len ) br[i++] = 0;
1152                         FFT fft;
1153                         fft.do_fft(len, 0, mix->br, 0, mix->br, mix->bi);
1154                         mix->aidx = idx;
1155                 }
1156         }
1157
1158         if( ret && !dialog->failed ) {
1159                 eprintf("Audio render failed:\n%s", edl->path);
1160                 dialog->failed = 1;
1161         }
1162
1163         for( int i=channels; --i>=0; ) delete samples[i];
1164         edl->remove_user();
1165 }
1166
1167 // scan mixer channels for best target
1168 MixersAlignTarget::MixersAlignTarget(int n, int cpus,
1169                 MixersAlignScanClient *scan, Samples **samples, int len)
1170  : LoadServer(n, cpus)
1171 {
1172         this->scan = scan;
1173         this->samples = samples;
1174         this->len = len;
1175 }
1176 MixersAlignTarget::~MixersAlignTarget()
1177 {
1178 }
1179
1180 MixersAlignTargetClient::MixersAlignTargetClient()
1181  : LoadClient()
1182 {
1183 }
1184 MixersAlignTargetClient::~MixersAlignTargetClient()
1185 {
1186 }
1187
1188 LoadClient* MixersAlignTarget::new_client()
1189 {
1190         return new MixersAlignTargetClient();
1191 }
1192
1193 MixersAlignTargetPackage::MixersAlignTargetPackage(MixersAlignTarget *targ)
1194 {
1195         ss = 0;  sd2 = 0;
1196         pos = -1;
1197         best = new double[targ->len];
1198 }
1199 MixersAlignTargetPackage::~MixersAlignTargetPackage()
1200 {
1201         delete [] best;
1202 }
1203
1204 LoadPackage* MixersAlignTarget::new_package()
1205 {
1206         return new MixersAlignTargetPackage(this);
1207 }
1208
1209 void MixersAlignTarget::init_packages()
1210 {
1211 }
1212
1213 void MixersAlignTargetClient::process_package(LoadPackage *package)
1214 {
1215         MixersAlignTarget *targ = (MixersAlignTarget *)server;
1216         MixersAlignScanClient *scan = targ->scan;
1217         MixersAlignScanFarm *farm = (MixersAlignScanFarm *)scan->server;
1218         MixersAlign *dialog = farm->dialog;
1219         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1220         if( dialog->failed ) return;
1221         pkg = (MixersAlignTargetPackage *)package;
1222
1223         int ch = get_package_number();
1224         double *data = targ->samples[ch]->get_data();
1225         int len1 = scan->len1;
1226 // computes sum(s**2), sum(d2**2) d2=discrete 2nd deriv
1227 // d0=s[i+0]-s[i+1], d1=s[i+1]-s[i+2], d2=d0-d1
1228 // d = s[i+0] - 2*s[i+1] + s[i+2]
1229         double ss = 0, sd2 = 0;
1230         double a = 0, b = 0, c = 0;
1231         for( int i=0; i<len1; ++i ) {
1232                 a = b;  b = c;  c = data[i];
1233                 double d = a - 2*b + c;
1234                 ss += c*c;  sd2 += d*d;
1235         }
1236 //best is highest sd2
1237         if( pkg->sd2 < sd2 ) {
1238                 pkg->sd2 = sd2;
1239                 pkg->ss = ss;
1240                 pkg->pos = scan->pos;
1241 //printf("targ %s:%d at %jd,ss=%f sd2=%f\n",
1242 //  scan->pkg->mixer->mixer->title, ch, scan->pos, ss, sd2);
1243                 double *best = pkg->best;
1244                 int i = 0, len = targ->len;
1245                 while( i < len1 ) best[i++] = *data++;
1246                 while( i < len ) best[i++] = 0;
1247         }
1248 }
1249
1250 void MixersAlign::scan_master(Track *track)
1251 {
1252         EDL *edl = mixer_master_clip(track);
1253         MixersAlignARender audio(mwindow, edl);
1254
1255         int channels = edl->get_audio_channels();
1256         int64_t sample_rate = edl->get_sample_rate();
1257         int64_t audio_samples = edl->get_audio_samples();
1258         int64_t cur_pos = audio_start * sample_rate;
1259         int64_t end_pos = audio_end * sample_rate;
1260         if( end_pos > audio_samples ) end_pos = audio_samples;
1261         if( cur_pos >= end_pos ) {
1262                 eprintf(_("scan master track empty"));
1263                 failed = 1;
1264                 return;
1265         }
1266         int len = sample_len, len2 = len/2;
1267         double *audio_r = new double[len];
1268         double *audio_i = new double[len];
1269         Samples *samples[2][MAX_CHANNELS];
1270         for( int k=0; k<2; ++k ) {
1271                 for( int i=0; i<MAX_CHANNELS; ++i )
1272                         samples[k][i] = i<channels ? new Samples(len2) : 0;
1273         }
1274
1275         int m = 0;
1276         for( int i=0,n=mixers.size(); i<n; ++i )
1277                 if( mixers[i]->br ) ++m;
1278         int cpus = bmin(mwindow->preferences->processors, m);
1279         MixersAlignMatchRevFarm farm(m, cpus, this, audio_r, audio_i, len);
1280
1281         FFT fft;
1282         Timer timer;
1283         start_progress(end_pos - cur_pos);
1284
1285         int k = 0;
1286         int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1287         if( nxt_pos > end_pos ) nxt_pos = end_pos;
1288         int len1 = nxt_pos - pos;
1289         int ret = audio.render(samples[k], len1, pos);
1290         while( !ret && !failed && cur_pos < end_pos ) {
1291                 pos = cur_pos;
1292                 cur_pos = nxt_pos;  nxt_pos += len2;
1293                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1294                 len1 = nxt_pos - cur_pos;
1295                 ret = audio.render(samples[1-k], len1, cur_pos);
1296                 if( ret ) break;
1297                 update_progress(len2);
1298                 int i = 0;
1299                 double *lp = samples[k][0]->get_data();
1300                 for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1301                 double *np = samples[k=1-k][0]->get_data();
1302                 for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1303                 while( i < len ) audio_r[i++] = 0;
1304                 fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1305                 farm.pos = pos;
1306                 farm.process_packages();
1307                 if( progress->is_cancelled() ) failed = -1;
1308         }
1309
1310         if( ret && !failed ) {
1311                 eprintf("Audio render failed:\n%s", edl->path);
1312                 failed = 1;
1313         }
1314
1315         char text[BCSTRLEN];
1316         double secs = timer.get_difference()/1000.;
1317         sprintf(text, _("Match mixer done: %0.3f secs"), secs);
1318         stop_progress(text);
1319
1320         delete [] audio_r;
1321         delete [] audio_i;
1322         for( int k=0; k<2; ++k ) {
1323                 for( int i=channels; --i>=0; )
1324                         delete samples[k][i];
1325         }
1326         edl->remove_user();
1327 }
1328
1329 MixersAlignMatchFwdPackage::MixersAlignMatchFwdPackage()
1330 {
1331         mixer = 0;
1332 }
1333
1334 MixersAlignMatchFwdPackage::~MixersAlignMatchFwdPackage()
1335 {
1336 }
1337
1338 MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm(MixersAlign *dialog, int n)
1339  : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
1340 {
1341         this->dialog = dialog;
1342         dialog->farming->lock("MixersAlignMatchFwdFarm::MixersAlignMatchFwdFarm");
1343 }
1344 MixersAlignMatchFwdFarm::~MixersAlignMatchFwdFarm()
1345 {
1346         dialog->farming->unlock();
1347 }
1348
1349 LoadPackage* MixersAlignMatchFwdFarm::new_package()
1350 {
1351         return new MixersAlignMatchFwdPackage();
1352 }
1353
1354 void MixersAlignMatchFwdFarm::init_packages()
1355 {
1356         for( int i = 0; i < get_total_packages(); ++i ) {
1357                 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
1358                 MixersAlignMatchFwdPackage *package = (MixersAlignMatchFwdPackage *)get_package(i);
1359                 package->mixer = dialog->mixers[m];
1360         }
1361 }
1362
1363 LoadClient* MixersAlignMatchFwdFarm::new_client()
1364 {
1365         return new MixersAlignMatchFwdClient(this);
1366 }
1367
1368 MixersAlignMatchFwdClient::MixersAlignMatchFwdClient(MixersAlignMatchFwdFarm *farm)
1369  : LoadClient(farm)
1370 {
1371 }
1372
1373 MixersAlignMatchFwdClient::~MixersAlignMatchFwdClient()
1374 {
1375 }
1376
1377 void MixersAlignMatchFwdClient::process_package(LoadPackage *package)
1378 {
1379         MixersAlignMatchFwdFarm *farm = (MixersAlignMatchFwdFarm *)server;
1380         MixersAlign *dialog = farm->dialog;
1381         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1382         if( dialog->failed ) return;
1383         pkg = (MixersAlignMatchFwdPackage *)package;
1384
1385         MixersAlignMixer *amix = pkg->mixer;
1386         EDL *edl = dialog->mixer_audio_clip(amix->mixer);
1387         MixersAlignARender audio(dialog->mwindow, edl);
1388         int channels = edl->get_audio_channels();
1389         int64_t sample_rate = edl->get_sample_rate();
1390         int64_t audio_samples = edl->get_audio_samples();
1391         int64_t cur_pos = dialog->audio_start * sample_rate;
1392         int64_t end_pos = dialog->audio_end * sample_rate;
1393         if( end_pos > audio_samples ) end_pos = audio_samples;
1394         int len = dialog->master_len, len2 = len/2;
1395         double *audio_r = new double[len];
1396         double *audio_i = new double[len];
1397
1398         Samples *samples[2][MAX_CHANNELS];
1399         for( int k=0; k<2; ++k ) {
1400                 for( int i=0; i<MAX_CHANNELS; ++i )
1401                         samples[k][i] = i<channels ? new Samples(len2) : 0;
1402         }
1403
1404         FFT fft;
1405         int k = 0;
1406         int64_t pos = cur_pos, nxt_pos = cur_pos+len2;
1407         if( nxt_pos > end_pos ) nxt_pos = end_pos;
1408         int len1 = nxt_pos - pos;
1409         int ret = audio.render(samples[k], len1, pos);
1410         while( !ret && !dialog->failed && cur_pos < end_pos ) {
1411                 dialog->update_progress(len1);
1412                 pos = cur_pos;
1413                 cur_pos = nxt_pos;
1414                 nxt_pos += len2;
1415                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
1416                 len1 = nxt_pos - cur_pos;
1417                 ret = audio.render(samples[1-k], len1, cur_pos);
1418                 if( ret ) break;
1419                 Track *track = edl->tracks->first;
1420                 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
1421                         int id = track->mixer_id, atk = dialog->atracks.size();
1422                         while( --atk >= 0 && id != dialog->atracks[atk]->track->mixer_id );
1423                         if( atk < 0 ) continue;
1424                         int i = 0;
1425                         double *lp = samples[k][ch]->get_data();
1426                         for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
1427                         double *np = samples[1-k][ch]->get_data();
1428                         for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
1429                         while( i < len ) audio_r[i++] = 0;
1430                         fft.do_fft(len, 0, audio_r, 0, audio_r, audio_i);
1431                         conj_product(len, audio_r, audio_i, audio_r, audio_i,
1432                                         dialog->master_r, dialog->master_i);
1433                         fft.do_fft(len, 1, audio_r, audio_i);
1434                         double mx = 0;  int64_t mi = -1;
1435                         for( int i=0; i<len2; ++i ) {
1436                                 double v = fabs(audio_r[i]);
1437                                 if( mx < v ) { mx = v;  mi = i + pos; }
1438                         }
1439                         mx /= dialog->master_ss;
1440                         MixersAlignATrack *atrack= dialog->atracks[atk];
1441                         if( atrack->mx < mx ) {
1442                                 atrack->mx = mx;
1443                                 atrack->mi = mi;
1444                         }
1445                 }
1446                 k = 1-k;
1447                 if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1448         }
1449         if( ret && !dialog->failed ) {
1450                 eprintf("Audio render failed:\n%s", edl->path);
1451                 dialog->failed = 1;
1452         }
1453         delete [] audio_r;
1454         delete [] audio_i;
1455         for( int k=0; k<2; ++k ) {
1456                 for( int i=channels; --i>=0; )
1457                         delete samples[k][i];
1458         }
1459         edl->remove_user();
1460 }
1461
1462
1463 MixersAlignMatchRevFarm::MixersAlignMatchRevFarm(int n, int cpus,
1464                 MixersAlign *dialog, double *ar, double *ai, int len)
1465  : LoadServer(n, cpus)
1466 {
1467         this->dialog = dialog;
1468         this->ar = ar;
1469         this->ai = ai;
1470         this->len = len;
1471         mixer_lock = new Mutex("MixersAlignMatchRevFarm::mixer_lock");
1472         pos = -1;
1473 }
1474 MixersAlignMatchRevFarm::~MixersAlignMatchRevFarm()
1475 {
1476         delete mixer_lock;
1477 }
1478
1479 MixersAlignMatchRevPackage::MixersAlignMatchRevPackage()
1480 {
1481         mix = 0;
1482 }
1483 MixersAlignMatchRevPackage::~MixersAlignMatchRevPackage()
1484 {
1485 }
1486
1487 void MixersAlignMatchRevFarm::init_packages()
1488 {
1489         for( int i=0,m=0,n=dialog->mixers.size(); m<n; ++m ) {
1490                 if( !dialog->mixers[m]->br ) continue;
1491                 MixersAlignMatchRevPackage *pkg = (MixersAlignMatchRevPackage *)get_package(i++);
1492                 pkg->mix = dialog->mixers[m];
1493         }
1494 }
1495
1496 LoadClient *MixersAlignMatchRevFarm::new_client()
1497 {
1498         return new MixersAlignMatchRevClient(this);
1499 }
1500 LoadPackage *MixersAlignMatchRevFarm::new_package()
1501 {
1502         return new MixersAlignMatchRevPackage();
1503 }
1504
1505 void MixersAlignMatchRevClient::process_package(LoadPackage *package)
1506 {
1507         MixersAlignMatchRevFarm *farm = (MixersAlignMatchRevFarm *)server;
1508         MixersAlign *dialog = farm->dialog;
1509         if( dialog->progress->is_cancelled() ) dialog->failed = -1;
1510         if( dialog->failed ) return;
1511         pkg = (MixersAlignMatchRevPackage *)package;
1512         MixersAlignMixer *mix = pkg->mix;
1513         if( mix->aidx < 0 ) return;
1514         int64_t ss = dialog->atracks[mix->aidx]->ss;
1515
1516         conj_product(farm->len, re, im, farm->ar, farm->ai, mix->br, mix->bi);
1517         FFT fft;
1518         fft.do_fft(farm->len, 1, re, im);
1519         double mx = 0;  int64_t mi = -1;
1520         for( int i=0,n=farm->len/2; i<n; ++i ) {
1521                 double r = fabs(re[i]) / ss;
1522                 if( mx < r ) {
1523                         mx = r;
1524                         mi = i + farm->pos;
1525                 }
1526         }
1527         farm->mixer_lock->lock("MixersAlignMatchRevFarm::process_package");
1528         if( mix->mx < mx ) {
1529                 mix->mx = mx;
1530                 mix->mi = mi;
1531 //printf("best %d: %f at %jd\n", get_package_number(), mx, mi);
1532         }
1533         farm->mixer_lock->unlock();
1534 }
1535
1536 MixersAlignMatchRevClient::MixersAlignMatchRevClient(MixersAlignMatchRevFarm *farm)
1537 {
1538         re = new double[farm->len];
1539         im = new double[farm->len];
1540 }
1541 MixersAlignMatchRevClient::~MixersAlignMatchRevClient()
1542 {
1543         delete [] re;
1544         delete [] im;
1545 }
1546
1547 void MixersAlign::start_progress(int64_t total_len)
1548 {
1549         total_rendered = 0;
1550         mwindow->gui->lock_window("MixersAlign::start_progress");
1551         progress = mwindow->mainprogress->
1552                 start_progress(_("match mixer audio"), total_len);
1553         mwindow->gui->unlock_window();
1554 }
1555 void MixersAlign::stop_progress(const char *msg)
1556 {
1557         mwindow->gui->lock_window("MixersAlign::stop_progress");
1558         progress->update(0);
1559         mwindow->mainprogress->end_progress(progress);
1560         progress = 0;
1561         if( msg ) mwindow->gui->show_message(msg);
1562         mwindow->gui->unlock_window();
1563 }
1564
1565 void MixersAlign::reset_targets()
1566 {
1567         for( int m=0,n=mixers.size(); m<n; ++m ) {
1568                 MixersAlignMixer *mix = mixers[m];
1569                 delete mix->br;  mix->br = 0;
1570                 delete mix->bi;  mix->bi = 0;
1571                 mix->mi = 0;
1572                 mix->aidx = -1;
1573         }
1574         for( int i=0,n=atracks.size(); i<n; ++i ) {
1575                 MixersAlignATrack *atrk = atracks[i];
1576                 atrk->nudge = 0; atrk->ss = 0;
1577                 atrk->mx = 0;    atrk->mi = -1;
1578         }
1579 }
1580
1581 void MixersAlign::scan_targets()
1582 {
1583         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1584         int midx = mmixer_of(idx);
1585         int64_t total_len = mixer_tracks_total(midx);
1586         start_progress(total_len);
1587         int m = mixers.size();
1588         if( midx >= 0 ) --m;
1589         int cpus = bmin(mwindow->preferences->processors, m);
1590         MixersAlignScanFarm scan(this, cpus, m);
1591         scan.process_packages();
1592         stop_progress(0);
1593 }
1594
1595 void MixersAlign::update_progress(int64_t len)
1596 {
1597         total_lock->lock();
1598         total_rendered += len;
1599         total_lock->unlock();
1600         progress->update(total_rendered);
1601 }
1602
1603 static inline uint64_t high_bit_mask(uint64_t n)
1604 {
1605         n |= n >> 1;   n |= n >> 2;
1606         n |= n >> 4;   n |= n >> 8;
1607         n |= n >> 16;  n |= n >> 32;
1608         return n;
1609 }
1610
1611 void MixersAlign::load_master_audio(Track *track)
1612 {
1613         EDL *edl = mixer_master_clip(track);
1614         MixersAlignARender audio(mwindow, edl);
1615         int channels = edl->get_audio_channels();
1616         int64_t sample_rate = edl->get_sample_rate();
1617         int64_t audio_samples = edl->get_audio_samples();
1618         int64_t cur_pos = master_start * sample_rate;
1619         int64_t end_pos = master_end * sample_rate;
1620         if( end_pos > audio_samples ) end_pos = audio_samples;
1621         if( cur_pos >= end_pos ) {
1622                 eprintf(_("master audio track empty"));
1623                 failed = 1;
1624                 return;
1625         }
1626         int64_t audio_len = end_pos - cur_pos;
1627         if( audio_len > sample_rate * 60 ) {
1628                 eprintf(_("master audio track length > 60 seconds"));
1629                 failed = 1;
1630                 return;
1631         }
1632         int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
1633         if( master_len != fft_len ) {
1634                 master_len = fft_len;
1635                 delete [] master_r;  master_r = new double[master_len];
1636                 delete [] master_i;  master_i = new double[master_len];
1637         }
1638
1639         Samples *samples[MAX_CHANNELS];
1640         for( int i=0; i<MAX_CHANNELS; ++i )
1641                 samples[i] = i<channels ? new Samples(audio_len) : 0;
1642         int ret = audio.render(samples, audio_len, cur_pos);
1643         if( ret ) failed = 1;
1644         edl->remove_user();
1645         if( !failed ) {
1646                 double *dp = samples[0]->get_data();
1647                 int i = 0;  double ss = 0;
1648                 for( ; i<audio_len; ++i ) { ss += *dp * *dp;  master_r[i] = *dp++; }
1649                 master_ss = ss;
1650                 for( ; i<fft_len; ++i ) master_r[i] = 0;
1651                 for( int i=channels; --i>=0; ) delete samples[i];
1652         }
1653         do_fft(fft_len, 0, master_r, 0, master_r, master_i);
1654 }
1655
1656 void MixersAlign::scan_mixer_audio()
1657 {
1658         for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
1659         int n = 0;
1660         while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
1661         if( !n ) {
1662                 eprintf(_("no mixers selected"));
1663                 return;
1664         }
1665
1666         Timer timer;
1667         MixersAlignMatchFwdFarm farm(this, n);
1668         int64_t total_len = mixer_tracks_total(-1);
1669         start_progress(total_len);
1670
1671         farm.process_packages();
1672         if( progress->is_cancelled() ) failed = -1;
1673
1674         char text[BCSTRLEN];
1675         double secs = timer.get_difference()/1000.;
1676         sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1677         stop_progress(text);
1678 }
1679
1680 void MixersAlign::match_fwd()
1681 {
1682         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1683         if( idx < 0 ) {
1684                 eprintf(_("selection (master) not set"));
1685                 return;
1686         }
1687         master_start = mwindow->edl->local_session->get_inpoint();
1688         if( master_start < 0 ) {
1689                 eprintf(_("in point selection (master start) must be set"));
1690                 return;
1691         }
1692         master_end = mwindow->edl->local_session->get_outpoint();
1693         if( master_end < 0 ) {
1694                 eprintf(_("out point selection (master end) must be set"));
1695                 return;
1696         }
1697         if( master_start >= master_end ) {
1698                 eprintf(_("in/out point selection (master start/end) invalid"));
1699                 return;
1700         }
1701         audio_start = mwindow->edl->local_session->get_selectionstart();
1702         audio_end = mwindow->edl->local_session->get_selectionend();
1703         if( audio_start >= audio_end ) {
1704                 eprintf(_("selection (audio start/end) invalid"));
1705                 return;
1706         }
1707
1708         failed = 0;
1709         if( !failed )
1710                 load_master_audio(mtracks[idx]->track);
1711         if( !failed )
1712                 scan_mixer_audio();
1713         if( !failed )
1714                 update_fwd();
1715         if( failed < 0 ) {
1716                 mwindow->gui->lock_window("MixersAlign::update_match_fwd");
1717                 mwindow->gui->show_message(_("mixer selection match canceled"));
1718                 mwindow->gui->unlock_window();
1719         }
1720         else if( failed > 0 )
1721                 eprintf(_("Error in match render."));
1722 }
1723
1724 void MixersAlign::match_rev()
1725 {
1726         int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1727         if( midx < 0 ) {
1728                 eprintf(_("selection (master) not set"));
1729                 return;
1730         }
1731         Track *track = mtracks[midx]->track;
1732         audio_start = mwindow->edl->local_session->get_selectionstart();
1733         audio_end = mwindow->edl->local_session->get_selectionend();
1734         if( audio_start >= audio_end ) {
1735                 eprintf(_("selection (audio start/end) invalid"));
1736                 return;
1737         }
1738
1739         reset_targets();
1740         failed = 0;
1741
1742         if( !failed )
1743                 scan_targets();
1744         if( !failed )
1745                 scan_master(track);
1746         if( !failed )
1747                 update_rev();
1748
1749         if( failed < 0 ) {
1750                 mwindow->gui->lock_window("MixersAlign::update_match_rev");
1751                 mwindow->gui->show_message(_("mixer selection match canceled"));
1752                 mwindow->gui->unlock_window();
1753         }
1754         else if( failed > 0 )
1755                 eprintf(_("Error in match render."));
1756 }
1757
1758 void MixersAlign::update_fwd()
1759 {
1760         double sample_rate = mwindow->edl->get_sample_rate();
1761         int64_t mi = master_start * sample_rate;
1762 // mixer best matches
1763         for( int m=0; m<mixers.size(); ++m ) {
1764                 MixersAlignMixer *mix = mixers[m];
1765                 Mixer *mixer = mix->mixer;
1766                 mix->mx = 0;  mix->mi = -1;  mix->aidx = -1;
1767                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1768                         int id = mixer->mixer_ids[i], k = atracks.size();
1769                         while( --k >= 0 && atracks[k]->track->mixer_id != id );
1770                         if( k < 0 ) continue;
1771                         MixersAlignATrack *atrk = atracks[k];
1772                         atrk->nudge = atrk->mi < 0 ? -1 :
1773                                 (mi - atrk->mi) / sample_rate;
1774                         if( mix->mx >= atrk->mx ) continue;
1775                         mix->aidx = k;
1776                         mix->mx = atrk->mx;
1777                         mix->mi = atrk->mi;
1778                         mix->nudge = atrk->nudge;
1779                 }
1780         }
1781         update();
1782 }
1783
1784 void MixersAlign::update_rev()
1785 {
1786         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
1787         int midx = mmixer_of(idx);
1788         double sample_rate = mwindow->edl->get_sample_rate();
1789         for( int m=0,n=mixers.size(); m<n; ++m ) {
1790                 if( m == midx ) continue;
1791                 if( !ma_gui->mixer_list->is_selected(m) ) continue;
1792                 MixersAlignMixer *mix = mixers[m];
1793                 if( mix->aidx < 0 ) continue;
1794                 MixersAlignATrack *atrk = atracks[mix->aidx];
1795                 mix->nudge = atrk->mi < 0 ? -1 :
1796                         (mix->mi - atrk->mi) / sample_rate;
1797                 atrk->mx = mix->mx;
1798         }
1799         update();
1800 }
1801
1802 void MixersAlign::update()
1803 {
1804         ma_gui->lock_window("MixersAlign::update");
1805         ma_gui->mixer_list->load_list();
1806         ma_gui->mixer_list->set_all_selected(1);
1807         ma_gui->mixer_list->update();
1808
1809         ma_gui->atrack_list->load_list();
1810         for( int m=0; m<mixers.size(); ++m ) {
1811                 int aidx = mixers[m]->aidx;
1812                 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1813         }
1814         ma_gui->atrack_list->update();
1815         ma_gui->unlock_window();
1816 }
1817