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