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