188782c1a09e3c69c8d85bb821c7c9ff4ff58240
[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 MixersAlignMixer::MixersAlignMixer(Mixer *mix)
47 {
48         this->mixer = mix;
49         this->nudge = 0;
50         mx = 0;  mi = -1;
51         aidx = -1;
52 }
53
54 const char *MixersAlignMixerList::mix_titles[MIX_SZ] = {
55         N_("Mixer"), N_("Nudge"),
56 };
57
58 int MixersAlignMixerList::mix_widths[MIX_SZ] = {
59         130, 50,
60 };
61
62 MixersAlignMixerList::MixersAlignMixerList(MixersAlignWindow *gui,
63                 MixersAlign *dialog, int x, int y, int w, int h)
64  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
65 {
66         set_selection_mode(LISTBOX_MULTIPLE);
67         this->dialog = dialog;
68         this->gui = gui;
69         for( int i=MIX_SZ; --i>=0; ) {
70                 col_widths[i] = mix_widths[i];
71                 col_titles[i] = _(mix_titles[i]);
72         }
73 }
74
75 MixersAlignMixerList::~MixersAlignMixerList()
76 {
77         clear();
78 }
79
80 void MixersAlignMixerList::clear()
81 {
82         for( int i=MIX_SZ; --i>=0; )
83                 cols[i].remove_all_objects();
84 }
85
86 void MixersAlignMixerList::add_mixer(MixersAlignMixer *mixer)
87 {
88         char mixer_text[BCSTRLEN];
89         snprintf(mixer_text, sizeof(mixer_text), "%d: %s",
90                 mixer->mixer->idx, mixer->mixer->title);
91         cols[MIX_MIXER].append(new BC_ListBoxItem(mixer_text));
92         char nudge_text[BCSTRLEN];
93         sprintf(nudge_text, _("%0.4f"), mixer->nudge);
94         cols[MIX_NUDGE].append(new BC_ListBoxItem(nudge_text));
95 }
96
97 void MixersAlignMixerList::load_list()
98 {
99         clear();
100         for( int i=0,sz=dialog->mixers.size(); i<sz; ++i )
101                 add_mixer(dialog->mixers[i]);
102 }
103
104 void MixersAlignMixerList::update()
105 {
106         int xpos = get_xposition(), ypos = get_yposition();
107         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MIX_SZ, xpos,ypos);
108 }
109
110 int MixersAlignMixerList::selection_changed()
111 {
112         for( int m=0; m<dialog->mixers.size(); ++m ) {
113                 MixersAlignMixer *mix = dialog->mixers[m];
114                 if( !is_selected(m) ) {
115                         for( int i=0; i<dialog->atracks.size(); ++i ) {
116                                 if( m != dialog->amixer_of(i) ) continue;
117                                 gui->atrack_list->set_selected(i, 0);
118                         }
119                         mix->aidx = -1;
120                         mix->mx = 0;
121                         mix->mi = -1;
122                         mix->nudge = 0;
123                 }
124                 else if( mix->aidx < 0 ) {
125                         MixersAlignATrack *best = 0;  int idx = -1;
126                         for( int i=0; i<dialog->atracks.size(); ++i ) {
127                                 if( m != dialog->amixer_of(i) ) continue;
128                                 MixersAlignATrack *atrk = dialog->atracks[i];
129                                 if( atrk->mi < 0 ) continue;
130                                 gui->atrack_list->set_selected(i, 0);
131                                 if( best && best->mx >= atrk->mx ) continue;
132                                 best = atrk;  idx = i;
133                         }
134                         if( idx >= 0 && best ) {
135                                 gui->atrack_list->set_selected(idx, 1);
136                                 MixersAlignMixer *mix = dialog->mixers[m];
137                                 mix->aidx = idx;
138                                 mix->mx = best->mx;
139                                 mix->mi = best->mi;
140                                 mix->nudge = best->nudge;
141                         }
142                         else {
143                                 for( int i=0; i<dialog->atracks.size(); ++i ) {
144                                         if( m != dialog->amixer_of(i) ) continue;
145                                         gui->atrack_list->set_selected(i, 1);
146                                 }
147                         }
148                 }
149         }
150         gui->atrack_list->update();
151
152         load_list();
153         for( int i=0; i<dialog->atracks.size(); ++i ) {
154                 if( !gui->atrack_list->is_selected(i) ) continue;
155                 int m = dialog->amixer_of(i);
156                 if( m >= 0 ) set_selected(m, 1);
157         }
158         update();
159         return 0;
160 }
161
162 MixersAlignMTrack::MixersAlignMTrack(Track *trk, int no)
163 {
164         this->track = trk;
165         this->no = no;
166 }
167
168
169 const char *MixersAlignMTrackList::mtk_titles[MTK_SZ] = {
170         N_("No"), N_("Mixer"), N_("Track"),
171 };
172
173 int MixersAlignMTrackList::mtk_widths[MTK_SZ] = {
174         32, 48, 110,
175 };
176
177 MixersAlignMTrackList::MixersAlignMTrackList(MixersAlignWindow *gui,
178                 MixersAlign *dialog, int x, int y, int w, int h)
179  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
180 {
181         set_selection_mode(LISTBOX_SINGLE);
182         this->dialog = dialog;
183         this->gui = gui;
184         for( int i=MTK_SZ; --i>=0; ) {
185                 col_widths[i] = mtk_widths[i];
186                 col_titles[i] = _(mtk_titles[i]);
187         }
188 }
189
190 MixersAlignMTrackList::~MixersAlignMTrackList()
191 {
192         clear();
193 }
194
195 void MixersAlignMTrackList::clear()
196 {
197         for( int i=MTK_SZ; --i>=0; )
198                 cols[i].remove_all_objects();
199 }
200
201 void MixersAlignMTrackList::add_mtrack(MixersAlignMTrack *mtrk)
202 {
203         char no_text[BCSTRLEN];
204         snprintf(no_text, sizeof(no_text),"%d", mtrk->no+1);
205         cols[MTK_NO].append(new BC_ListBoxItem(no_text));
206         char mixer_text[BCSTRLEN];
207         Track *track = mtrk->track;
208         int midx = -1, m = dialog->mixer_of(track, midx);
209         snprintf(mixer_text, sizeof(mixer_text),"%d:%d", m+1,midx);
210         cols[MTK_MIXER].append(new BC_ListBoxItem(mixer_text));
211         cols[MTK_TRACK].append(new BC_ListBoxItem(track->title));
212 }
213
214 void MixersAlignMTrackList::load_list()
215 {
216         clear();
217         for( int i=0,sz=dialog->mtracks.size(); i<sz; ++i )
218                 add_mtrack(dialog->mtracks[i]);
219 }
220
221 void MixersAlignMTrackList::update()
222 {
223         int xpos = get_xposition(), ypos = get_yposition();
224         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],MTK_SZ, xpos,ypos);
225 }
226
227
228 MixersAlignATrack::MixersAlignATrack(Track *trk, int no)
229 {
230         this->track = trk;
231         this->no = no;
232         this->nudge = 0;
233         mx = 0;  mi = -1;
234 }
235
236
237 const char *MixersAlignATrackList::atk_titles[ATK_SZ] = {
238         N_("Track"), N_("Audio"), N_("Nudge"), N_("R"), N_("pos"),
239 };
240
241 int MixersAlignATrackList::atk_widths[ATK_SZ] = {
242         40, 100, 60, 60, 60,
243 };
244
245 MixersAlignATrackList::MixersAlignATrackList(MixersAlignWindow *gui,
246                 MixersAlign *dialog, int x, int y, int w, int h)
247  : BC_ListBox(x, y, w, h, LISTBOX_TEXT)
248 {
249         set_selection_mode(LISTBOX_MULTIPLE);
250         this->dialog = dialog;
251         this->gui = gui;
252         for( int i=ATK_SZ; --i>=0; ) {
253                 col_widths[i] = atk_widths[i];
254                 col_titles[i] = _(atk_titles[i]);
255         }
256 }
257
258 MixersAlignATrackList::~MixersAlignATrackList()
259 {
260         clear();
261 }
262
263 void MixersAlignATrackList::clear()
264 {
265         for( int i=ATK_SZ; --i>=0; )
266                 cols[i].remove_all_objects();
267 }
268
269 void MixersAlignATrackList::add_atrack(MixersAlignATrack *atrack)
270 {
271         char atrack_text[BCSTRLEN];
272         Track *track = atrack->track;
273         int midx = -1, m = dialog->mixer_of(track, midx);
274         snprintf(atrack_text, sizeof(atrack_text), "%d/%d", atrack->no+1,m+1);
275         cols[ATK_TRACK].append(new BC_ListBoxItem(atrack_text));
276         cols[ATK_AUDIO].append(new BC_ListBoxItem(track->title));
277         char nudge_text[BCSTRLEN];
278         sprintf(nudge_text, "%0.4f", atrack->nudge);
279         cols[ATK_NUDGE].append(new BC_ListBoxItem(nudge_text));
280         char mx_text[BCSTRLEN];
281         sprintf(mx_text, "%0.4f", atrack->mx);
282         cols[ATK_MX].append(new BC_ListBoxItem(mx_text));
283         char mi_text[BCSTRLEN];
284         sprintf(mi_text, "%jd", atrack->mi);
285         cols[ATK_MI].append(new BC_ListBoxItem(mi_text));
286 }
287
288
289 void MixersAlignATrackList::load_list()
290 {
291         clear();
292         for( int i=0,sz=dialog->atracks.size(); i<sz; ++i )
293                 add_atrack(dialog->atracks[i]);
294 }
295
296 void MixersAlignATrackList::update()
297 {
298         int xpos = get_xposition(), ypos = get_yposition();
299         BC_ListBox::update(&cols[0], &col_titles[0],&col_widths[0],ATK_SZ, xpos,ypos);
300 }
301
302 int MixersAlignATrackList::selection_changed()
303 {
304         int idx = get_buttonpress() == LEFT_BUTTON ? get_highlighted_item() : -1;
305         int m = idx >= 0 ? dialog->amixer_of(idx) : -1;
306         if( m >= 0 ) {
307                 int sel = 0;
308                 MixersAlignMixer *mix = dialog->mixers[m];
309                 for( int i=0; i<dialog->atracks.size(); ++i ) {
310                         int k = dialog->amixer_of(i);
311                         if( k < 0 ) { set_selected(i, 0);  continue; }
312                         if( m != k ) continue;
313                         MixersAlignATrack *atrk = dialog->atracks[i];
314                         if( atrk->mi < 0 ) continue;
315                         int is_sel = is_selected(i);
316                         set_selected(i, 0);
317                         if( i != idx ) continue;
318                         if( is_sel ) {
319                                 mix->aidx = idx;
320                                 mix->mx = atrk->mx;
321                                 mix->mi = atrk->mi;
322                                 mix->nudge = atrk->nudge;
323                                 sel = 1;
324                         }
325                         else {
326                                 mix->aidx = -1;
327                                 mix->mx = 0;
328                                 mix->mi = -1;
329                                 mix->nudge = 0;
330                         }
331                 }
332                 if( sel )
333                         set_selected(idx, 1);
334         }
335         update();
336
337         gui->mixer_list->load_list();
338         for( int i=0; i<dialog->atracks.size(); ++i ) {
339                 if( !is_selected(i) ) continue;
340                 int m = dialog->amixer_of(i);
341                 if( m < 0 ) continue;
342                 gui->mixer_list->set_selected(m, 1);
343         }
344         gui->mixer_list->update();
345         return 0;
346 }
347
348 MixersAlignReset::MixersAlignReset(MixersAlignWindow *gui,
349                 MixersAlign *dialog, int x, int y)
350  : BC_GenericButton(x, y, _("Reset"))
351 {
352         this->gui = gui;
353         this->dialog = dialog;
354 }
355
356 int MixersAlignReset::calculate_width(BC_WindowBase *gui)
357 {
358         return BC_GenericButton::calculate_w(gui, _("Reset"));
359 }
360
361 int MixersAlignReset::handle_event()
362 {
363         dialog->load_mixers();
364         dialog->load_mtracks();
365         dialog->load_atracks();
366         gui->load_lists();
367         gui->default_selection();
368         return 1;
369 }
370
371 MixersAlignMatch::MixersAlignMatch(MixersAlignWindow *gui,
372                 MixersAlign *dialog, int x, int y)
373  : BC_GenericButton(x, y, _("Match"))
374 {
375         this->gui = gui;
376         this->dialog = dialog;
377 }
378
379 int MixersAlignMatch::handle_event()
380 {
381         if( !dialog->thread->running() ) {
382                 gui->reset->disable();
383                 gui->match->disable();
384                 gui->apply->disable();
385                 gui->undo->disable();
386                 dialog->thread->start();
387         }
388         return 1;
389 }
390
391 MixersAlignApply::MixersAlignApply(MixersAlignWindow *gui,
392                 MixersAlign *dialog, int x, int y)
393  : BC_GenericButton(x, y, _("Apply"))
394 {
395         this->gui = gui;
396         this->dialog = dialog;
397 }
398
399 int MixersAlignApply::calculate_width(BC_WindowBase *gui)
400 {
401         return BC_GenericButton::calculate_w(gui, _("Apply"));
402 }
403
404 int MixersAlignApply::handle_event()
405 {
406         dialog->apply();
407         return 1;
408 }
409
410 MixersAlignUndo::MixersAlignUndo(MixersAlignWindow *gui,
411                 MixersAlign *dialog, int x, int y)
412  : BC_GenericButton(x, y, _("Undo"))
413 {
414         this->gui = gui;
415         this->dialog = dialog;
416 }
417
418 int MixersAlignUndo::handle_event()
419 {
420         MWindow *mwindow = dialog->mwindow;
421         mwindow->edl->copy_all(dialog->undo_edl);
422         mwindow->gui->lock_window("MixersAlignUndo::handle_event");
423         mwindow->update_gui(1);
424         mwindow->gui->unlock_window();
425         return gui->reset->handle_event();
426 }
427
428
429 MixersAlignWindow::MixersAlignWindow(MixersAlign *dialog, int x, int y)
430  : BC_Window(_("Align Mixers"), x, y, 880, 380, 880, 380, 1)
431 {
432         this->dialog = dialog;
433 }
434 MixersAlignWindow::~MixersAlignWindow()
435 {
436 }
437
438 void MixersAlignWindow::create_objects()
439 {
440         int x = 10, y = 10, w4 = (get_w()-x-10)/4, lw = w4 + 20;
441         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = get_w()-x;
442         mixer_title = new BC_Title(x1,y, _("Mixers:"), MEDIUMFONT, YELLOW);
443         add_subwindow(mixer_title);
444         mtrack_title = new BC_Title(x2,y, _("Master Track:"), MEDIUMFONT, YELLOW);
445         add_subwindow(mtrack_title);
446         atrack_title = new BC_Title(x3,y, _("Audio Tracks:"), MEDIUMFONT, YELLOW);
447         add_subwindow(atrack_title);
448         y += mixer_title->get_h() + 10;
449         int y1 = y, y2 = get_h() - BC_OKButton::calculate_h() - 15;
450         int lh = y2 - y1;
451         mixer_list = new MixersAlignMixerList(this, dialog, x1, y, x2-x1-20, lh);
452         add_subwindow(mixer_list);
453         mtrack_list = new MixersAlignMTrackList(this, dialog, x2, y, x3-x2-20, lh);
454         add_subwindow(mtrack_list);
455         atrack_list = new MixersAlignATrackList(this, dialog, x3, y, x4-x3-20, lh);
456         add_subwindow(atrack_list);
457         int xr = x2-10 - MixersAlignReset::calculate_width(this);
458         add_subwindow(reset = new MixersAlignReset(this, dialog, xr, y2+20));
459         add_subwindow(match = new MixersAlignMatch(this, dialog, x2+10, y2+20));
460         int xa = x3-10 - MixersAlignApply::calculate_width(this);
461         add_subwindow(apply = new MixersAlignApply(this, dialog, xa, y2+20));
462         add_subwindow(undo = new MixersAlignUndo(this, dialog, x3+10, y2+20));
463
464         add_subwindow(new BC_OKButton(this));
465         add_subwindow(new BC_CancelButton(this));
466 }
467
468 int MixersAlignWindow::resize_event(int w, int h)
469 {
470         int x = 10, y = 10, w4 = (w-x-10)/4, lw = w4 + 20;
471         int x1 = x, x2 = x1 + lw , x3 = x2 + lw, x4 = w-x;
472         mixer_title->reposition_window(x1, y);
473         mtrack_title->reposition_window(x2, y);
474         atrack_title->reposition_window(x3, y);
475         y += mixer_title->get_h() + 10;
476         int y1 = y, y2 = h - BC_OKButton::calculate_h() - 15;
477         int lh = y2 - y1;
478         mixer_list->reposition_window(x1, y, x2-x1-20, lh);
479         mtrack_list->reposition_window(x2, y, x3-x2-20, lh);
480         atrack_list->reposition_window(x3, y, x4-x3-20, lh);
481         int xr = x2-10 - MixersAlignReset::calculate_width(this);
482         reset->reposition_window(xr, y2+20);
483         match->reposition_window(x2+10, y2+20);
484         int xa = x3-10 - MixersAlignApply::calculate_width(this);
485         apply->reposition_window(xa, y2+20);
486         undo->reposition_window(x3+10, y2+20);
487         return 0;
488 }
489
490 void MixersAlignWindow::load_lists()
491 {
492         mixer_list->load_list();
493         mtrack_list->load_list();
494         atrack_list->load_list();
495 }
496
497 void MixersAlignWindow::default_selection()
498 {
499 // mixers selects all mixers
500         mixer_list->set_all_selected(1);
501 // master selects first mixer audio track
502         for( int i=0; i<dialog->mtracks.size(); ++i ) {
503                 if( dialog->mmixer_of(i) >= 0 ) {
504                         mtrack_list->set_selected(i, 1);
505                         break;
506                 }
507         }
508 // audio selects all mixer audio tracks
509         for( int i=0; i<dialog->atracks.size(); ++i ) {
510                 if( dialog->amixer_of(i) >= 0 )
511                         atrack_list->set_selected(i, 1);
512         }
513         update_gui();
514 }
515
516 void MixersAlignWindow::update_gui()
517 {
518         mixer_list->update();
519         mtrack_list->update();
520         atrack_list->update();
521 }
522
523
524 MixersAlign::MixersAlign(MWindow *mwindow)
525 {
526         this->mwindow = mwindow;
527         this->ma_gui = 0;
528         farming = new Mutex("MixersAlign::farming");
529         total_lock = new Mutex("MixersAlign::total_lock");
530         thread = new MixersAlignThread(this);
531         total_rendered = 0;
532         master_r = 0;
533         master_i = 0;
534         master_len = 0;
535         progress = 0;
536         failed = 0;
537         undo_edl = 0;
538         master_start = master_end = 0;
539         audio_start = audio_end = 0;
540 }
541
542 MixersAlign::~MixersAlign()
543 {
544         failed = -1;
545         farming->lock("MixersAlign::~MixersAlign");
546         close_window();
547         delete farming;
548         delete thread;
549         delete total_lock;
550         delete progress;
551         delete [] master_r;
552         delete [] master_i;
553 }
554
555 void MixersAlign::start_dialog(int wx, int wy)
556 {
557         this->wx = wx;
558         this->wy = wy;
559         this->undo_edl = new EDL();
560         undo_edl->create_objects();
561         undo_edl->copy_all(mwindow->edl);
562         start();
563 }
564
565 BC_Window *MixersAlign::new_gui()
566 {
567         load_mixers();
568         load_mtracks();
569         load_atracks();
570         ma_gui = new MixersAlignWindow(this, wx, wy);
571         ma_gui->lock_window("MixersAlign::new_gui");
572         ma_gui->create_objects();
573         ma_gui->load_lists();
574         ma_gui->default_selection();
575         ma_gui->show_window(1);
576         ma_gui->unlock_window();
577         return ma_gui;
578 }
579
580 void MixersAlign::apply()
581 {
582         int idx = ma_gui->mtrack_list->get_selection_number(0, 0);
583         int midx = -1, mid = mixer_of(mtracks[idx]->track, midx);
584         EDL *edl = mwindow->edl;
585         
586         for( int m, i=0; (m=ma_gui->mixer_list->get_selection_number(0,i))>=0; ++i ) {
587                 if( m == mid ) continue;  // master does not move
588                 MixersAlignMixer *mix = mixers[m];
589                 Mixer *mixer = mix->mixer;
590                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
591                         int id = mixer->mixer_ids[i];
592                         Track *track = edl->tracks->first;
593                         while( track && track->mixer_id != id ) track = track->next;
594                         if( !track ) continue;
595                         double nudge = mix->nudge;
596                         int record = track->record;  track->record = 1;
597                         if( nudge < 0 ) {
598                                 edl->clear(0, -nudge,
599                                         edl->session->labels_follow_edits,
600                                         edl->session->plugins_follow_edits,
601                                         edl->session->autos_follow_edits);
602                         }
603                         else if( nudge > 0 ) {
604                                 edl->paste_silence(0, nudge,
605                                         edl->session->labels_follow_edits,
606                                         edl->session->plugins_follow_edits,
607                                         edl->session->autos_follow_edits);
608                         }
609                         track->record = record;
610                 }
611         }
612         edl->optimize();
613
614         mwindow->gui->lock_window("MixersAlign::handle_done_event");
615         mwindow->update_gui(1);
616         mwindow->gui->unlock_window();
617 }
618
619
620 MixersAlignThread::MixersAlignThread(MixersAlign *dialog)
621  : Thread(1, 0, 0)
622 {
623         this->dialog = dialog;
624 }
625 MixersAlignThread::~MixersAlignThread()
626 {
627         join();
628 }
629
630 void MixersAlignThread::run()
631 {
632         dialog->update_match();
633         MixersAlignWindow *ma_gui = dialog->ma_gui;
634         ma_gui->lock_window("MixersAlignThread::run");
635         ma_gui->reset->enable();
636         ma_gui->match->enable();
637         ma_gui->apply->enable();
638         ma_gui->undo->enable();
639         ma_gui->unlock_window();
640 }
641
642
643 void MixersAlign::handle_done_event(int result)
644 {
645         if( thread->running() ) {
646                 failed = -1;
647                 thread->join();
648                 return;
649         }
650         if( !result ) {
651                 EDL *edl = mwindow->edl;
652                 mwindow->edl = undo_edl;
653                 mwindow->undo_before();
654                 mwindow->edl = edl;
655                 mwindow->undo_after(_("align mixers"), LOAD_ALL);
656         }
657 }
658
659 void MixersAlign::handle_close_event(int result)
660 {
661         undo_edl->remove_user();
662         undo_edl = 0;
663         ma_gui = 0;
664 }
665
666 void MixersAlign::load_mixers()
667 {
668         mixers.remove_all_objects();
669         Mixers &edl_mixers = mwindow->edl->mixers;
670         for( int i=0; i<edl_mixers.size(); ++i )
671                 mixers.append(new MixersAlignMixer(edl_mixers[i]));
672 }
673
674 void MixersAlign::load_mtracks()
675 {
676         mtracks.remove_all_objects();
677         Track *track=mwindow->edl->tracks->first;
678         for( int no=0; track; ++no, track=track->next ) {
679                 if( track->data_type != TRACK_AUDIO ) continue;
680                 mtracks.append(new MixersAlignMTrack(track, no));
681         }
682 }
683
684 void MixersAlign::load_atracks()
685 {
686         atracks.remove_all_objects();
687         Track *track=mwindow->edl->tracks->first;
688         for( int no=0; track; ++no, track=track->next ) {
689                 if( track->data_type != TRACK_AUDIO ) continue;
690                 if( mixer_of(track) < 0 ) continue;
691                 atracks.append(new MixersAlignATrack(track, no));
692         }
693 }
694
695
696 int MixersAlign::mixer_of(Track *track, int &midx)
697 {
698         int id = track->mixer_id, k = mixers.size(), idx = -1;
699         while( idx < 0 && --k >= 0 )
700                 idx = mixers[k]->mixer->mixer_ids.number_of(id);
701         midx = idx;
702         return k;
703 }
704
705 EDL *MixersAlign::mixer_audio_clip(Mixer *mixer)
706 {
707         EDL *edl = new EDL(mwindow->edl);
708         edl->create_objects();
709         Track *track = mwindow->edl->tracks->first;
710         for( int ch=0; track && ch<MAX_CHANNELS; track=track->next ) {
711                 int id = track->mixer_id;
712                 if( track->data_type != TRACK_AUDIO ) continue;
713                 if( mixer->mixer_ids.number_of(id) < 0 ) continue;
714                 Track *mixer_audio = edl->tracks->add_audio_track(0, 0);
715                 mixer_audio->copy_settings(track);
716                 mixer_audio->play = 1;
717                 mixer_audio->edits->copy_from(track->edits);
718                 PanAuto* pan_auto = (PanAuto*)mixer_audio->automation->
719                                 autos[AUTOMATION_PAN]->default_auto;
720                 pan_auto->values[ch++] = 1.0;
721         }
722         return edl;
723 }
724
725 EDL *MixersAlign::mixer_master_clip(Track *track)
726 {
727         EDL *edl = new EDL(mwindow->edl);
728         edl->create_objects();
729         Track *master_audio = edl->tracks->add_audio_track(0, 0);
730         master_audio->copy_settings(track);
731         master_audio->play = 1;
732         master_audio->edits->copy_from(track->edits);
733         PanAuto* pan_auto = (PanAuto*)master_audio->automation->
734                         autos[AUTOMATION_PAN]->default_auto;
735         pan_auto->values[0] = 1.0;
736         return edl;
737 }
738
739 int64_t MixersAlign::mixer_tracks_total()
740 {
741         int64_t total_len = 0;
742         int64_t sample_rate = mwindow->edl->get_sample_rate();
743         int m = -1, idx = 0;
744         for( ; (m=ma_gui->mixer_list->get_selection_number(0, idx))>=0 ; ++idx ) {
745                 Mixer *mixer = mixers[m]->mixer;
746                 double render_end = 0;
747                 Track *track = mwindow->edl->tracks->first;
748                 for( ; track; track=track->next ) {
749                         int id = track->mixer_id;
750                         if( track->data_type != TRACK_AUDIO ) continue;
751                         if( mixer->mixer_ids.number_of(id) < 0 ) continue;
752                         double track_end = track->get_length();
753                         if( render_end < track_end ) render_end = track_end;
754                 }
755                 if( render_end > audio_end ) render_end = audio_end;
756                 double len = render_end - audio_start;
757                 if( len > 0 ) total_len += len * sample_rate;
758         }
759         return total_len;
760 }
761
762 MixersAlignARender::MixersAlignARender(MWindow *mwindow, EDL *edl)
763  : RenderEngine(0, mwindow->preferences, 0, 0)
764 {
765         TransportCommand command;
766         command.command = NORMAL_FWD;
767         command.get_edl()->copy_all(edl);
768         command.change_type = CHANGE_ALL;
769         command.realtime = 0;
770         set_vcache(mwindow->video_cache);
771         set_acache(mwindow->audio_cache);
772         arm_command(&command);
773 }
774
775 MixersAlignARender::~MixersAlignARender()
776 {
777 }
778
779 int MixersAlignARender::render(Samples **samples, int64_t len, int64_t pos)
780 {
781         return arender ? arender->process_buffer(samples, len, pos) : -1;
782 }
783
784 MixersAlignPackage::MixersAlignPackage()
785 {
786         mixer = 0;
787 }
788
789 MixersAlignPackage::~MixersAlignPackage()
790 {
791 }
792
793 MixersAlignFarm::MixersAlignFarm(MixersAlign *dialog, int n)
794  : LoadServer(bmin(dialog->mwindow->preferences->processors, n), n)
795 {
796         this->dialog = dialog;
797         dialog->farming->lock("MixersAlignFarm::MixersAlignFarm");
798 }
799 MixersAlignFarm::~MixersAlignFarm()
800 {
801         dialog->farming->unlock();
802 }
803
804 LoadPackage* MixersAlignFarm::new_package()
805 {
806         return new MixersAlignPackage();
807 }
808
809 void MixersAlignFarm::init_packages()
810 {
811         for( int i = 0; i < get_total_packages(); ++i ) {
812                 int m = dialog->ma_gui->mixer_list->get_selection_number(0, i);
813                 MixersAlignPackage *package = (MixersAlignPackage *)get_package(i);
814                 package->mixer = dialog->mixers[m];
815         }
816 }
817
818 LoadClient* MixersAlignFarm::new_client()
819 {
820         return new MixersAlignClient(this);
821 }
822
823 MixersAlignClient::MixersAlignClient(MixersAlignFarm *farm)
824  : LoadClient(farm)
825 {
826 }
827
828 MixersAlignClient::~MixersAlignClient()
829 {
830 }
831
832 void MixersAlignClient::process_package(LoadPackage *pkg)
833 {
834         MixersAlignFarm *farm = (MixersAlignFarm *)server;
835         MixersAlign *dialog = farm->dialog;
836         MixersAlignPackage *package = (MixersAlignPackage *)pkg;
837         dialog->process_package(farm, package);
838 }
839
840 static inline void conj_product(int n, double *rp, double *ip,
841                 double *arp, double *aip, double *brp, double *bip)
842 {
843         for( int i=0; i<n; ++i ) {
844                 double ar = arp[i], ai = aip[i];
845                 double br = brp[i], bi = -bip[i];
846                 rp[i] = ar*br - ai*bi;
847                 ip[i] = ar*bi + ai*br;
848         }
849 }
850
851 void MixersAlign::process_package(MixersAlignFarm *farm,
852                  MixersAlignPackage *package)
853 {
854         if( progress->is_cancelled() ) failed = -1;
855         if( failed ) return;
856         MixersAlignMixer *amix = package->mixer;
857         EDL *edl = mixer_audio_clip(amix->mixer);
858
859         MixersAlignARender audio(mwindow, edl);
860         int sample_rate = edl->get_sample_rate();
861         int channels = edl->get_audio_channels();
862         int64_t audio_samples = edl->get_audio_samples();
863         int64_t cur_pos = audio_start * sample_rate;
864         int64_t end_pos = audio_end * sample_rate;
865         if( end_pos > audio_samples ) end_pos = audio_samples;
866         int len = master_len, len2 = len/2;
867         double *audio_r = new double[len];
868         double *audio_i = new double[len];
869         Samples *samples[2][MAX_CHANNELS];
870         for( int k=0; k<2; ++k ) {
871                 for( int i=0; i<MAX_CHANNELS; ++i )
872                         samples[k][i] = i<channels ? new Samples(len2) : 0;
873         }
874         int k = 0;
875         int64_t nxt_pos = cur_pos+len2;
876         if( nxt_pos > end_pos ) nxt_pos = end_pos;
877         int len1 = nxt_pos - cur_pos;
878         int ret = audio.render(samples[k], len1, cur_pos);
879         while( !ret && !failed && cur_pos < end_pos ) {
880                 update_progress(len1);
881                 int64_t pos = cur_pos;
882                 cur_pos = nxt_pos;  nxt_pos += len2;
883                 if( nxt_pos > end_pos ) nxt_pos = end_pos;
884                 len1 = nxt_pos - cur_pos;
885                 ret = audio.render(samples[1-k], len1, cur_pos);
886                 if( ret ) break;
887                 Track *track = edl->tracks->first;
888                 for( int ch=0; ch<channels && track; ++ch, track=track->next ) {
889                         int id = track->mixer_id, atk = atracks.size();
890                         while( --atk >= 0 && id != atracks[atk]->track->mixer_id );
891                         if( atk < 0 ) continue;
892                         int i = 0;
893                         double *lp = samples[k][ch]->get_data();
894                         for( int j=0; j<len2; ++j ) audio_r[i++] = *lp++;
895                         double *np = samples[1-k][ch]->get_data();
896                         for( int j=0; j<len1; ++j ) audio_r[i++] = *np++;
897                         while( i < len ) audio_r[i++] = 0;
898                         do_fft(len, 0, audio_r, 0, audio_r, audio_i);
899                         conj_product(len, audio_r, audio_i,
900                                 audio_r, audio_i, master_r, master_i);
901                         do_fft(len, 1, audio_r, audio_i, audio_r, audio_i);
902                         double mx = 0;  int64_t mi = -1;
903                         for( int i=0; i<len2; ++i ) {
904                                 double v = fabs(audio_r[i]);
905                                 if( mx < v ) { mx = v;  mi = i + pos; }
906                         }
907                         mx /= master_ss;
908                         MixersAlignATrack *atrack= atracks[atk];
909                         if( atrack->mx < mx ) {
910                                 atrack->mx = mx;
911                                 atrack->mi = mi;
912                         }
913                 }
914                 k = 1-k;
915                 if( progress->is_cancelled() ) failed = -1;
916         }
917         if( ret && !failed ) {
918                 eprintf("Audio render failed:\n%s", edl->path);
919                 failed = 1;
920         }
921         delete [] audio_r;
922         delete [] audio_i;
923         for( int k=0; k<2; ++k ) {
924                 for( int i=channels; --i>=0; )
925                         delete samples[k][i];
926         }
927         edl->remove_user();
928 }
929
930
931 void MixersAlign::update_progress(int64_t len)
932 {
933         total_lock->lock();
934         total_rendered += len;
935         total_lock->unlock();
936         progress->update(total_rendered);
937 }
938
939 static inline uint64_t high_bit_mask(uint64_t n)
940 {
941         n |= n >> 1;   n |= n >> 2;
942         n |= n >> 4;   n |= n >> 8;
943         n |= n >> 16;  n |= n >> 32;
944         return n;
945 }
946
947 void MixersAlign::load_master_audio(Track *track)
948 {
949         EDL *edl = mixer_master_clip(track);
950         MixersAlignARender audio(mwindow, edl);
951         int sample_rate = edl->get_sample_rate();
952         int channels = edl->get_audio_channels();
953         int64_t audio_samples = edl->get_audio_samples();
954         int64_t cur_pos = master_start * sample_rate;
955         int64_t end_pos = master_end * sample_rate;
956         if( end_pos > audio_samples ) end_pos = audio_samples;
957         if( cur_pos >= end_pos ) {
958                 eprintf(_("master audio track empty"));
959                 failed = 1;
960                 return;
961         }
962         int64_t audio_len = end_pos - cur_pos;
963         if( audio_len > sample_rate * 60 ) {
964                 eprintf(_("master audio track length > 60 seconds"));
965                 failed = 1;
966                 return;
967         }
968         int64_t fft_len = (high_bit_mask(audio_len)+1) << 1;
969         if( master_len != fft_len ) {
970                 master_len = fft_len;
971                 delete [] master_r;  master_r = new double[master_len];
972                 delete [] master_i;  master_i = new double[master_len];
973         }
974         {       Samples *samples[MAX_CHANNELS];
975                 for( int i=0; i<MAX_CHANNELS; ++i )
976                         samples[i] = i<channels ? new Samples(audio_len) : 0;
977                 int ret = audio.render(samples, audio_len, cur_pos);
978                 if( ret ) failed = 1;
979                 edl->remove_user();
980                 double *dp = samples[0]->get_data();
981                 int i = 0;  double ss = 0;
982                 for( ; i<audio_len; ++i ) { ss += *dp * *dp;  master_r[i] = *dp++; }
983                 master_ss = ss;
984                 for( ; i<fft_len; ++i ) master_r[i] = 0;
985                 for( int i=channels; --i>=0; )
986                         delete samples[i];
987         }
988         do_fft(fft_len, 0, master_r, 0, master_r, master_i);
989 }
990
991 void MixersAlign::scan_mixer_audio()
992 {
993         for( int i=0; i<mixers.size(); ++i ) mixers[i]->aidx = -1;
994         int n = 0;
995         while( ma_gui->mixer_list->get_selection_number(0, n) >= 0 ) ++n;
996         if( !n ) {
997                 eprintf(_("no mixers selected"));
998                 return;
999         }
1000
1001         Timer timer;
1002         total_rendered = 0;
1003         MixersAlignFarm farm(this, n);
1004         int64_t total_len = mixer_tracks_total();
1005         mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
1006         progress = mwindow->mainprogress->
1007                 start_progress(_("Render mixer audio"), total_len);
1008         mwindow->gui->unlock_window();
1009
1010         farm.process_packages();
1011         if( progress->is_cancelled() ) failed = -1;
1012
1013         char text[BCSTRLEN];
1014         double secs = timer.get_difference()/1000.;
1015         sprintf(text, _("Render mixer done: %0.3f secs"), secs);
1016         mwindow->gui->lock_window("MixersAlign::scan_mixer_audio");
1017         progress->update(0);
1018         mwindow->mainprogress->end_progress(progress);
1019         progress = 0;
1020         mwindow->gui->show_message(text);
1021         mwindow->gui->unlock_window();
1022 }
1023
1024 void MixersAlign::update_match()
1025 {
1026         int midx = ma_gui->mtrack_list->get_selection_number(0, 0);
1027         if( midx < 0 ) {
1028                 eprintf(_("selection (master) not set"));
1029                 return;
1030         }
1031         master_start = mwindow->edl->local_session->get_inpoint();
1032         if( master_start < 0 ) {
1033                 eprintf(_("in point selection (master start) must be set"));
1034                 return;
1035         }
1036         master_end = mwindow->edl->local_session->get_outpoint();
1037         if( master_end < 0 ) {
1038                 eprintf(_("out point selection (master end) must be set"));
1039                 return;
1040         }
1041         if( master_start >= master_end ) {
1042                 eprintf(_("in/out point selection (master start/end) invalid"));
1043                 return;
1044         }
1045         audio_start = mwindow->edl->local_session->get_selectionstart();
1046         audio_end = mwindow->edl->local_session->get_selectionend();
1047         if( audio_start >= audio_end ) {
1048                 eprintf(_("selection (audio start/end) invalid"));
1049                 return;
1050         }
1051
1052         failed = 0;
1053         if( !failed ) load_master_audio(mtracks[midx]->track);
1054         if( !failed ) scan_mixer_audio();
1055         if( !failed ) update();
1056         if( failed < 0 ) {
1057                 mwindow->gui->lock_window("MixersAlign::update_match");
1058                 mwindow->gui->show_message(_("mixer selection match canceled"));
1059                 mwindow->gui->unlock_window();
1060         }
1061         else if( failed > 0 )
1062                 eprintf(_("Error in match render."));
1063 }
1064
1065 void MixersAlign::update()
1066 {
1067 // mixer best matches
1068         for( int m=0; m<mixers.size(); ++m ) {
1069                 MixersAlignMixer *mix = mixers[m];
1070                 Mixer *mixer = mix->mixer;
1071                 mix->mx = 0;  mix->mi = -1;  mix->aidx = -1;
1072                 for( int i=0; i<mixer->mixer_ids.size(); ++i ) {
1073                         int id = mixer->mixer_ids[i], k = atracks.size();
1074                         while( --k >= 0 && atracks[k]->track->mixer_id != id );
1075                         if( k < 0 ) continue;
1076                         MixersAlignATrack *atrk = atracks[k];
1077                         atrk->nudge = atrk->mi < 0 ? 0 :
1078                                 master_start - atrk->track->from_units(atrk->mi);
1079                         if( mix->mx >= atrk->mx ) continue;
1080                         mix->aidx = k;
1081                         mix->mx = atrk->mx;
1082                         mix->mi = atrk->mi;
1083                         mix->nudge = atrk->nudge;
1084                 }
1085         }
1086
1087         ma_gui->lock_window("MixersAlign::update");
1088         ma_gui->mixer_list->load_list();
1089         ma_gui->mixer_list->set_all_selected(1);
1090         ma_gui->mixer_list->update();
1091
1092         ma_gui->atrack_list->load_list();
1093         for( int m=0; m<mixers.size(); ++m ) {
1094                 int aidx = mixers[m]->aidx;
1095                 if( aidx >= 0 ) ma_gui->atrack_list->set_selected(aidx, 1);
1096         }
1097         ma_gui->atrack_list->update();
1098         ma_gui->unlock_window();
1099 }
1100