transcode target dir can be nested proxy pref, filebox/loadmode tweaks, meters tweak...
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / convert.C
1 /*
2  * CINELERRA
3  * Copyright (C) 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 "assets.h"
22 #include "audiodevice.h"
23 #include "autos.h"
24 #include "bchash.h"
25 #include "bcpot.h"
26 #include "bctimer.h"
27 #include "cache.h"
28 #include "convert.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "file.h"
32 #include "filesystem.h"
33 #include "formattools.h"
34 #include "indexfile.h"
35 #include "language.h"
36 #include "localsession.h"
37 #include "mainerror.h"
38 #include "mainindexes.h"
39 #include "mainprogress.h"
40 #include "mainundo.h"
41 #include "mutex.h"
42 #include "mwindow.h"
43 #include "mwindowgui.h"
44 #include "packagedispatcher.h"
45 #include "packagerenderer.h"
46 #include "panautos.h"
47 #include "panauto.h"
48 #include "preferences.h"
49 #include "render.h"
50 #include "theme.h"
51 #include "tracks.h"
52
53
54 #define WIDTH xS(400)
55 #define HEIGHT yS(360)
56 #define MAX_SCALE 16
57
58 ConvertRender::ConvertRender(MWindow *mwindow)
59  : Thread(1, 0, 0)
60 {
61         this->mwindow = mwindow;
62         suffix = 0;
63         format_asset = 0;
64         progress = 0;
65         progress_timer = new Timer;
66         convert_progress = 0;
67         counter_lock = new Mutex("ConvertDialog::counter_lock");
68         total_rendered = 0;
69         remove_originals = 0;
70         failed = 0;  canceled = 0;
71         result = 0;
72         beep = 0;
73         to_proxy = 0;
74 }
75
76 ConvertRender::~ConvertRender()
77 {
78         delete [] suffix;
79         delete progress;
80         delete counter_lock;
81         delete progress_timer;
82         delete convert_progress;
83         if( format_asset )
84                 format_asset->remove_user();
85         reset();
86 }
87
88 void ConvertRender::reset()
89 {
90         for( int i=0,n=orig_idxbls.size(); i<n; ++i )
91                 orig_idxbls[i]->remove_user();
92         orig_idxbls.remove_all();
93         for( int i=0,n=orig_copies.size(); i<n; ++i )
94                 orig_copies[i]->remove_user();
95         orig_copies.remove_all();
96         for( int i=0,n=needed_idxbls.size(); i<n; ++i )
97                 needed_idxbls[i]->remove_user();
98         needed_idxbls.remove_all();
99         for( int i=0,n=needed_copies.size(); i<n; ++i )
100                 needed_copies[i]->remove_user();
101         needed_copies.remove_all();
102 }
103
104 void ConvertRender::to_convert_path(char *new_path, Indexable *idxbl)
105 {
106         if( to_proxy ) {
107                 char *bp = idxbl->path, *cp = strrchr(bp, '/');
108                 if( cp ) bp = cp+1;
109                 char filename[BCTEXTLEN], proxy_path[BCTEXTLEN];
110                 strcpy(filename, bp);
111                 File::getenv_path(proxy_path, mwindow->preferences->nested_proxy_path);
112                 sprintf(new_path, "%s/%s", proxy_path, filename);
113         }
114         else
115                 strcpy(new_path, idxbl->path);
116         int n = strlen(suffix);
117         char *ep = new_path + strlen(new_path);
118         char *sfx = strrchr(new_path, '.');
119         if( sfx ) {
120 // insert suffix, path.sfx => path+suffix-sfx.ext
121                 char *bp = ep, *cp = (ep += n);
122                 while( --bp > sfx ) *--cp = *bp;
123                 *--cp = '-';
124         }
125         else {
126 // insert suffix, path => path+suffix.ext
127                 sfx = ep;  ep += n;
128         }
129         for( const char *cp=suffix; --n>=0; ++cp ) *sfx++ = *cp;
130         *ep++ = '.';
131         const char *ext = format_asset->format == FILE_FFMPEG ?
132                         format_asset->fformat :
133                         File::get_tag(format_asset->format);
134         while( *ext ) *ep++ = *ext++;
135         *ep = 0;
136 }
137
138 int ConvertRender::from_convert_path(char *path, Indexable *idxbl)
139 {
140         strcpy(path, idxbl->path);
141         char *ep = path + strlen(path);
142         const char *ext = format_asset->format == FILE_FFMPEG ?
143                         format_asset->fformat :
144                         File::get_tag(format_asset->format);
145         const char *rp = ext + strlen(ext);
146         do {
147                 --rp;  --ep;
148         } while( rp>=ext && ep>=path && *ep == *rp );
149         if( rp >= ext || ep < path && *--ep != '.' ) return 1;   // didnt find .ext
150         int n = strlen(suffix), len = 0;
151         char *lp = ep - n;  // <suffix-chars>+.ext
152         for( ; --lp>=path ; ++len ) {
153                 if( strncmp(lp, suffix, n) ) continue;
154                 if( !len || lp[n] == '-' ) break;
155         }
156         if( lp < path ) return 1;  // didnt find suffix
157         if( *(rp=lp+n) == '-' ) {
158 // remove suffix, path+suffix-sfx.ext >= path.sfx
159                 *lp++ = '.';  ++rp;
160                 while( rp < ep ) *lp++ = *rp++;
161         }
162 // remove suffix, path+suffix.ext => path
163         *lp = 0;
164         return 0;
165 }
166
167 double ConvertRender::get_video_length(Indexable *idxbl)
168 {
169         int64_t video_frames = idxbl->get_video_frames();
170         double frame_rate = idxbl->get_frame_rate();
171         if( video_frames < 0 && mwindow->edl->session->si_useduration )
172                 video_frames = mwindow->edl->session->si_duration * frame_rate;
173         if( video_frames < 0 ) video_frames = 1;
174         return !video_frames ? 0 : video_frames / frame_rate;
175 }
176
177 double ConvertRender::get_audio_length(Indexable *idxbl)
178 {
179         int64_t audio_samples = idxbl->get_audio_samples();
180         return !audio_samples ? 0 :
181                 (double)audio_samples / idxbl->get_sample_rate();
182 }
183
184 double ConvertRender::get_length(Indexable *idxbl)
185 {
186         return bmax(get_video_length(idxbl), get_audio_length(idxbl));
187 }
188
189 int ConvertRender::match_format(Asset *asset)
190 {
191 // close enough
192         return format_asset->audio_data == asset->audio_data &&
193                 format_asset->video_data == asset->video_data ? 1 : 0;
194 }
195
196 EDL *ConvertRender::convert_edl(EDL *edl, Indexable *idxbl)
197 {
198         Asset *copy_asset = edl->assets->get_asset(idxbl->path);
199         if( !copy_asset ) return 0;
200         if( !copy_asset->layers && !copy_asset->channels ) return 0;
201 // make a clip from 1st video track and audio tracks
202         EDL *copy_edl = new EDL(edl);
203         copy_edl->create_objects();
204         copy_edl->set_path(copy_asset->path);
205         char path[BCTEXTLEN];
206         FileSystem fs;  fs.extract_name(path, copy_asset->path);
207         strcpy(copy_edl->local_session->clip_title, path);
208         strcpy(copy_edl->local_session->clip_notes, _("Transcode clip"));
209
210         double video_length = get_video_length(idxbl);
211         double audio_length = get_audio_length(idxbl);
212         copy_edl->session->video_tracks =
213                  video_length > 0 ? 1 : 0;
214         copy_edl->session->audio_tracks =
215                  audio_length > 0 ? copy_asset->channels : 0;
216
217         copy_edl->create_default_tracks();
218         Track *current = copy_edl->session->video_tracks ?
219                 copy_edl->tracks->first : 0;
220         for( int vtrack=0; current; current=NEXT ) {
221                 if( current->data_type != TRACK_VIDEO ) continue;
222                 current->insert_asset(copy_asset, 0, video_length, 0, vtrack);
223                 break;
224         }
225         current = copy_edl->session->audio_tracks ?
226                 copy_edl->tracks->first : 0;
227         for( int atrack=0; current; current=NEXT ) {
228                 if( current->data_type != TRACK_AUDIO ) continue;
229                 current->insert_asset(copy_asset, 0, audio_length, 0, atrack);
230                 Autos *pan_autos = current->automation->autos[AUTOMATION_PAN];
231                 PanAuto *pan_auto = (PanAuto*)pan_autos->get_auto_for_editing(-1);
232                 for( int i=0; i < MAXCHANNELS; ++i ) pan_auto->values[i] = 0;
233                 pan_auto->values[atrack++] = 1;
234                 if( atrack >= MAXCHANNELS ) atrack = 0;
235         }
236         copy_edl->folder_no = AW_MEDIA_FOLDER;
237         return copy_edl;
238 }
239
240 int ConvertRender::add_original(EDL *edl, Indexable *idxbl)
241 {
242         char new_path[BCTEXTLEN];
243 // if idxbl already a convert
244         if( !from_convert_path(new_path, idxbl) ) return 0;
245 // don't convert if not readable
246         if( idxbl->is_asset && access(idxbl->path, R_OK) ) return 0;
247 // found readable unconverted asset
248         to_convert_path(new_path, idxbl);
249 // add to orig_idxbls & orig_copies if it isn't already there.
250         int got_it = 0;
251         for( int i = 0; !got_it && i<orig_copies.size(); ++i )
252                 got_it = !strcmp(orig_copies[i]->path, new_path);
253         if( got_it ) return 0;
254         idxbl->add_user();
255         orig_idxbls.append(idxbl);
256         int needed = 1;
257         Asset *convert = edl->assets->get_asset(new_path);
258         if( !convert ) {
259                 convert = new Asset(new_path);
260                 FileSystem fs;
261                 if( fs.get_size(new_path) > 0 ) {
262 // copy already on disk
263                         int64_t orig_mtime = fs.get_date(idxbl->path);
264                         int64_t convert_mtime = fs.get_date(new_path);
265 // render needed if it is too old
266                         if( orig_mtime < convert_mtime ) {
267                                 File file;
268                                 int ret = file.open_file(mwindow->preferences, convert, 1, 0);
269 // render not needed if can use copy
270                                 if( ret == FILE_OK ) {
271                                         if( match_format(file.asset) ) {
272                                                 mwindow->mainindexes->add_indexable(convert);
273                                                 mwindow->mainindexes->start_build();
274                                                 needed = 0;
275                                         }
276                                         else
277                                                 needed = -1;
278                                 }
279                         }
280                 }
281         }
282         else if( match_format(convert) ) {
283 // dont render if copy already an assets
284                 convert->add_user();
285                 needed = 0;
286         }
287         else
288                 needed = -1;
289         if( needed < 0 ) {
290                 eprintf(_("transcode target file exists but is incorrect format:\n%s\n"
291                           "remove file from disk before transcode to new format.\n"), new_path);
292                 return -1;
293         }
294         orig_copies.append(convert);
295         if( needed ) {
296                 convert->copy_format(format_asset, 0);
297 // new compression parameters
298                 convert->video_data = format_asset->video_data;
299                 if( convert->video_data ) {
300                         convert->layers = 1;
301                         convert->width = idxbl->get_w();
302                         if( convert->width & 1 ) ++convert->width;
303                         convert->actual_width = convert->width;
304                         convert->height = idxbl->get_h();
305                         if( convert->height & 1 ) ++convert->height;
306                         convert->actual_height = convert->height;
307                         convert->frame_rate = mwindow->edl->session->frame_rate;
308                 }
309                 convert->audio_data = format_asset->audio_data;
310                 if( convert->audio_data ) {
311                         convert->sample_rate = mwindow->edl->session->sample_rate;
312                 }
313                 convert->folder_no = AW_MEDIA_FOLDER;
314                 add_needed(idxbl, convert);
315         }
316         return 1;
317 }
318
319 void ConvertRender::add_needed(Indexable *idxbl, Asset *convert)
320 {
321         needed_idxbls.append(idxbl);
322         idxbl->add_user();
323         needed_copies.append(convert);
324         convert->add_user();
325 }
326
327
328 int ConvertRender::is_canceled()
329 {
330         return progress->is_cancelled();
331 }
332
333 int ConvertRender::find_convertable_assets(EDL *edl)
334 {
335         reset();
336         Asset *orig_asset = edl->assets->first;
337         int count = 0;
338         for( ; orig_asset; orig_asset=orig_asset->next ) {
339                 int ret = add_original(edl, orig_asset);
340                 if( ret < 0 ) return -1;
341                 if( ret ) ++count;
342         }
343         return count;
344 }
345
346 void ConvertRender::set_format(Asset *asset, const char *suffix, int to_proxy)
347 {
348         delete [] this->suffix;
349         this->suffix = cstrdup(suffix);
350         if( !format_asset )
351                 format_asset = new Asset();
352         format_asset->copy_from(asset, 0);
353         this->to_proxy = to_proxy;
354 }
355
356 void ConvertRender::start_convert(float beep, int remove_originals)
357 {
358         this->beep = beep;
359         this->remove_originals = remove_originals;
360         start();
361 }
362
363 void ConvertRender::run()
364 {
365         mwindow->stop_brender();
366         result = 0;
367         total_rendered = 0;
368         failed = 0;  canceled = 0;
369
370         progress_timer->update();
371         start_progress();
372
373         for( int i=0; !failed && !canceled && i<needed_copies.size(); ++i )
374                 create_copy(i);
375
376         canceled = progress->is_cancelled();
377 printf(_("convert: failed=%d canceled=%d\n"), failed, canceled);
378         double elapsed_time = progress_timer->get_scaled_difference(1);
379
380         char elapsed[BCSTRLEN], text[BCSTRLEN];
381         Units::totext(elapsed, elapsed_time, TIME_HMS2);
382         printf(_("TranscodeRender::run: done in %s\n"), elapsed);
383         if( canceled )
384                 strcpy(text, _("transcode cancelled"));
385         else if( failed )
386                 strcpy(text, _("transcode failed"));
387         else
388                 sprintf(text, _("transcode %d files, render time %s"),
389                         needed_copies.size(), elapsed);
390 // stop progress bar
391         stop_progress(text);
392
393         if( !failed ) {
394                 mwindow->finish_convert(remove_originals);
395         }
396         else if( !canceled ) {
397                 eprintf(_("Error making transcode."));
398         }
399
400         if( !canceled && beep > 0 ) {
401                 if( failed ) {
402                         mwindow->beep(4000., 0.5, beep);
403                         usleep(250000);
404                         mwindow->beep(1000., 0.5, beep);
405                         usleep(250000);
406                         mwindow->beep(4000., 0.5, beep);
407                 }
408                 else
409                         mwindow->beep(2000., 2.0, beep);
410         }
411         mwindow->restart_brender();
412 }
413
414 void ConvertRender::start_progress()
415 {
416         double total_len= 0;
417         for( int i = 0; i < needed_idxbls.size(); i++ ) {
418                 Indexable *orig_idxbl = needed_idxbls[i];
419                 double length = get_length(orig_idxbl);
420                 total_len += length;
421         }
422         int64_t total_samples = total_len * format_asset->sample_rate;
423         mwindow->gui->lock_window("Render::start_progress");
424         progress = mwindow->mainprogress->
425                 start_progress(_("Transcode files..."), total_samples);
426         mwindow->gui->unlock_window();
427         convert_progress = new ConvertProgress(mwindow, this);
428         convert_progress->start();
429 }
430
431 void ConvertRender::stop_progress(const char *msg)
432 {
433         delete convert_progress;  convert_progress = 0;
434         mwindow->gui->lock_window("ConvertRender::stop_progress");
435         progress->update(0);
436         mwindow->mainprogress->end_progress(progress);
437         progress = 0;
438         mwindow->gui->show_message(msg);
439         mwindow->gui->update_default_message();
440         mwindow->gui->unlock_window();
441 }
442
443
444 ConvertPackageRenderer::ConvertPackageRenderer(ConvertRender *render)
445  : PackageRenderer()
446 {
447         this->render = render;
448 }
449
450 ConvertPackageRenderer::~ConvertPackageRenderer()
451 {
452 }
453
454 int ConvertPackageRenderer::get_master()
455 {
456         return 1;
457 }
458
459 int ConvertPackageRenderer::get_result()
460 {
461         return render->result;
462 }
463
464 void ConvertPackageRenderer::set_result(int value)
465 {
466         if( value )
467                 render->result = value;
468 }
469
470 void ConvertPackageRenderer::set_progress(int64_t value)
471 {
472         render->counter_lock->lock("ConvertPackageRenderer::set_progress");
473 // Increase total rendered for all nodes
474         render->total_rendered += value;
475         render->counter_lock->unlock();
476 }
477
478 int ConvertPackageRenderer::progress_cancelled()
479 {
480         return render->progress && render->progress->is_cancelled();
481 }
482
483 ConvertProgress::ConvertProgress(MWindow *mwindow, ConvertRender *render)
484  : Thread(1, 0, 0)
485 {
486         this->mwindow = mwindow;
487         this->render = render;
488         last_value = 0;
489 }
490
491 ConvertProgress::~ConvertProgress()
492 {
493         cancel();
494         join();
495 }
496
497 void ConvertProgress::run()
498 {
499         Thread::disable_cancel();
500         for( ;; ) {
501                 if( render->total_rendered != last_value ) {
502                         render->progress->update(render->total_rendered);
503                         last_value = render->total_rendered;
504                 }
505
506                 Thread::enable_cancel();
507                 sleep(1);
508                 Thread::disable_cancel();
509         }
510 }
511
512 void ConvertRender::create_copy(int i)
513 {
514         Indexable *orig_idxbl = needed_idxbls[i];
515         Asset *needed_copy = needed_copies[i];
516         EDL *edl = convert_edl(mwindow->edl, orig_idxbl);
517         double length = get_length(orig_idxbl);
518         ConvertPackageRenderer renderer(this);
519         renderer.initialize(mwindow, edl, mwindow->preferences, needed_copy);
520         PackageDispatcher dispatcher;
521         dispatcher.create_packages(mwindow, edl, mwindow->preferences,
522                 SINGLE_PASS, needed_copy, 0, length, 0);
523         RenderPackage *package = dispatcher.get_package(0);
524         if( !renderer.render_package(package) ) {
525                 Asset *asset = mwindow->edl->assets->update(needed_copy);
526                 mwindow->mainindexes->add_indexable(asset);
527                 mwindow->mainindexes->start_build();
528         }
529         else
530                 failed = 1;
531         edl->remove_user();
532 }
533
534 ConvertWindow::ConvertWindow(MWindow *mwindow, ConvertDialog *dialog, int x, int y)
535  : BC_Window(_(PROGRAM_NAME ": Transcode settings"), x, y, WIDTH, HEIGHT,
536                 -1, -1, 0, 0, 1)
537 {
538         this->mwindow = mwindow;
539         this->dialog = dialog;
540         format_tools = 0;
541 }
542
543 ConvertWindow::~ConvertWindow()
544 {
545         lock_window("ConvertWindow::~ConvertWindow");
546         delete format_tools;
547         unlock_window();
548 }
549
550
551 void ConvertWindow::create_objects()
552 {
553         lock_window("ConvertWindow::create_objects");
554         int margin = mwindow->theme->widget_border;
555         int lmargin = margin + xS(10);
556
557         int x = lmargin;
558         int y = margin + yS(10);
559
560         BC_Title *text;
561         add_subwindow(text = new BC_Title(x, y,
562                 _("Render untagged assets and replace in project")));
563         y += text->get_h() + margin + yS(10);
564         int y1 = y;
565         y += BC_Title::calculate_h(this, _("Tag suffix:")) + margin + yS(10);
566
567         x = lmargin;
568         format_tools = new ConvertFormatTools(mwindow, this, dialog->asset);
569         format_tools->create_objects(x, y, 1, 1, 1, 1, 0, 1, 0, 1, // skip the path
570                 0, 0);
571
572         x = lmargin;
573         add_subwindow(text = new BC_Title(x, y1, _("Tag suffix:")));
574         x = format_tools->format_text->get_x();
575         add_subwindow(suffix_text = new ConvertSuffixText(this, dialog, x, y1));
576         x = lmargin;
577         y += margin + yS(10);
578
579         add_subwindow(remove_originals = new ConvertRemoveOriginals(this, x, y));
580         x = lmargin;
581         y += remove_originals->get_h() + margin;
582         add_subwindow(to_proxy_path = new ConvertToProxyPath(this, x, y));
583         y += to_proxy_path->get_h() + margin;
584
585         add_subwindow(beep_on_done = new ConvertBeepOnDone(this, x, y));
586         x += beep_on_done->get_w() + margin + xS(10);
587         add_subwindow(new BC_Title(x, y+yS(10), _("Beep on done volume")));
588 //      y += beep_on_done->get_h() + margin;
589
590         add_subwindow(new BC_OKButton(this));
591         add_subwindow(new BC_CancelButton(this));
592         show_window(1);
593         unlock_window();
594 }
595
596 ConvertSuffixText::ConvertSuffixText(ConvertWindow *gui,
597                 ConvertDialog *dialog, int x, int y)
598  : BC_TextBox(x, y, 160, 1, dialog->suffix)
599 {
600         this->gui = gui;
601         this->dialog = dialog;
602 }
603
604 ConvertSuffixText::~ConvertSuffixText()
605 {
606 }
607
608 int ConvertSuffixText::handle_event()
609 {
610         strcpy(dialog->suffix, get_text());
611         return 1;
612 }
613
614 ConvertFormatTools::ConvertFormatTools(MWindow *mwindow, ConvertWindow *gui, Asset *asset)
615  : FormatTools(mwindow, gui, asset)
616 {
617         this->gui = gui;
618 }
619
620 void ConvertFormatTools::update_format()
621 {
622         asset->save_defaults(mwindow->defaults, "CONVERT_", 1, 1, 0, 0, 0);
623         FormatTools::update_format();
624 }
625
626
627 ConvertMenuItem::ConvertMenuItem(MWindow *mwindow)
628  : BC_MenuItem(_("Transcode..."),  _("Alt-e"), 'e')
629 {
630         this->mwindow = mwindow;
631         set_alt();
632         dialog = 0;
633 }
634 ConvertMenuItem::~ConvertMenuItem()
635 {
636         delete dialog;
637 }
638
639 void ConvertMenuItem::create_objects()
640 {
641         dialog = new ConvertDialog(mwindow);
642 }
643
644 int ConvertMenuItem::handle_event()
645 {
646         mwindow->gui->unlock_window();
647         dialog->start();
648         mwindow->gui->lock_window("ConvertMenuItem::handle_event");
649         return 1;
650 }
651
652
653 ConvertDialog::ConvertDialog(MWindow *mwindow)
654 {
655         this->mwindow = mwindow;
656         gui = 0;
657         asset = new Asset;
658         strcpy(suffix, ".transcode");
659 // quicker than some, not as good as others
660         asset->format = FILE_FFMPEG;
661         strcpy(asset->fformat, "mp4");
662         strcpy(asset->vcodec, "h264.mp4");
663         asset->ff_video_bitrate = 2560000;
664         asset->video_data = 1;
665         strcpy(asset->acodec, "h264.mp4");
666         asset->ff_audio_bitrate = 256000;
667         asset->audio_data = 1;
668         remove_originals = 1;
669         beep = 0;
670         to_proxy = 0;
671 }
672
673 ConvertDialog::~ConvertDialog()
674 {
675         close_window();
676         asset->remove_user();
677 }
678
679 BC_Window* ConvertDialog::new_gui()
680 {
681         asset->format = FILE_FFMPEG;
682         asset->frame_rate = mwindow->edl->session->frame_rate;
683         asset->sample_rate = mwindow->edl->session->sample_rate;
684         asset->load_defaults(mwindow->defaults, "CONVERT_", 1, 1, 0, 1, 0);
685         remove_originals = mwindow->defaults->get("CONVERT_REMOVE_ORIGINALS", remove_originals);
686         beep = mwindow->defaults->get("CONVERT_BEEP", beep);
687         to_proxy = mwindow->defaults->get("CONVERT_TO_PROXY", to_proxy);
688         mwindow->defaults->get("CONVERT_SUFFIX", suffix);
689         mwindow->gui->lock_window("ConvertDialog::new_gui");
690         int cx, cy;
691         mwindow->gui->get_abs_cursor(cx, cy);
692         gui = new ConvertWindow(mwindow, this, cx - WIDTH/2, cy - HEIGHT/2);
693         gui->create_objects();
694         mwindow->gui->unlock_window();
695         return gui;
696 }
697
698 void ConvertDialog::handle_close_event(int result)
699 {
700         if( result ) return;
701         mwindow->defaults->update("CONVERT_SUFFIX", suffix);
702         mwindow->defaults->update("CONVERT_REMOVE_ORIGINALS", remove_originals);
703         mwindow->defaults->update("CONVERT_BEEP", beep);
704         mwindow->defaults->update("CONVERT_TO_PROXY", to_proxy);
705         asset->save_defaults(mwindow->defaults, "CONVERT_", 1, 1, 0, 1, 0);
706         mwindow->start_convert(asset, suffix, beep, to_proxy, remove_originals);
707 }
708
709
710 ConvertRemoveOriginals::ConvertRemoveOriginals(ConvertWindow *gui, int x, int y)
711  : BC_CheckBox(x, y, gui->dialog->remove_originals, _("Remove originals from project"))
712 {
713         this->gui = gui;
714 }
715
716 ConvertRemoveOriginals::~ConvertRemoveOriginals()
717 {
718 }
719
720 int ConvertRemoveOriginals::handle_event()
721 {
722         gui->dialog->remove_originals = get_value();
723         return 1;
724 }
725
726 ConvertToProxyPath::ConvertToProxyPath(ConvertWindow *gui, int x, int y)
727  : BC_CheckBox(x, y, gui->dialog->to_proxy, _("Into Nested Proxy directory"))
728 {
729         this->gui = gui;
730 }
731
732 ConvertToProxyPath::~ConvertToProxyPath()
733 {
734 }
735
736 int ConvertToProxyPath::handle_event()
737 {
738         gui->dialog->to_proxy = get_value();
739         return 1;
740 }
741
742 ConvertBeepOnDone::ConvertBeepOnDone(ConvertWindow *gui, int x, int y)
743  : BC_FPot(x, y, gui->dialog->beep*100.f, 0.f, 100.f)
744 {
745         this->gui = gui;
746 }
747
748 int ConvertBeepOnDone::handle_event()
749 {
750         gui->dialog->beep = get_value()/100.f;
751         return 1;
752 }
753