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