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