r1014: Enable horizontal scrolling with the mouse wheel by pressing Ctrl.
[cinelerra_cv/ct.git] / cinelerra / menueffects.C
blob8a6e8c0308193575ad01e858a0cc1ac5d0b27ebd
1 #include "asset.h"
2 #include "clip.h"
3 #include "confirmsave.h"
4 #include "bchash.h"
5 #include "edl.h"
6 #include "edlsession.h"
7 #include "errorbox.h"
8 #include "file.h"
9 #include "formatcheck.h"
10 #include "indexfile.h"
11 #include "keyframe.h"
12 #include "keys.h"
13 #include "labels.h"
14 #include "language.h"
15 #include "loadmode.h"
16 #include "localsession.h"
17 #include "mainmenu.h"
18 #include "mainsession.h"
19 #include "mainundo.h"
20 #include "mwindow.h"
21 #include "mwindowgui.h"
22 #include "menueffects.h"
23 #include "playbackengine.h"
24 #include "pluginarray.h"
25 #include "pluginserver.h"
26 #include "preferences.h"
27 #include "render.h"
28 #include "sighandler.h"
29 #include "theme.h"
30 #include "tracks.h"
34 MenuEffects::MenuEffects(MWindow *mwindow)
35  : BC_MenuItem(_("Render effect..."))
37         this->mwindow = mwindow;
40 MenuEffects::~MenuEffects()
45 int MenuEffects::handle_event()
47         thread->set_title("");
48         thread->start();
55 MenuEffectPacket::MenuEffectPacket(char *path, int64_t start, int64_t end)
57         this->start = start;
58         this->end = end;
59         strcpy(this->path, path);
62 MenuEffectPacket::~MenuEffectPacket()
71 MenuEffectThread::MenuEffectThread(MWindow *mwindow)
73         this->mwindow = mwindow;
74         sprintf(title, "");
77 MenuEffectThread::~MenuEffectThread()
85 int MenuEffectThread::set_title(char *title)
87         strcpy(this->title, title);
90 // for recent effect menu items and running new effects
91 // prompts for an effect if title is blank
92 void MenuEffectThread::run()
94 // get stuff from main window
95         ArrayList<PluginServer*> *plugindb = mwindow->plugindb;
96         BC_Hash *defaults = mwindow->defaults;
97         ArrayList<BC_ListBoxItem*> plugin_list;
98         ArrayList<PluginServer*> local_plugindb;
99         char string[1024];
100         int i;
101         int result = 0;
102 // Default configuration
103         Asset *default_asset = new Asset;
104 // Output
105         ArrayList<Asset*> assets;
108 // check for recordable tracks
109         if(!get_recordable_tracks(default_asset))
110         {
111                 sprintf(string, _("No recordable tracks specified."));
112                 ErrorBox error(PROGRAM_NAME ": Error");
113                 error.create_objects(string);
114                 error.run_window();
115                 Garbage::delete_object(default_asset);
116                 return;
117         }
119 // check for plugins
120         if(!plugindb->total)
121         {
122                 sprintf(string, _("No plugins available."));
123                 ErrorBox error(PROGRAM_NAME ": Error");
124                 error.create_objects(string);
125                 error.run_window();
126                 Garbage::delete_object(default_asset);
127                 return;
128         }
131 // get default attributes for output file
132 // used after completion
133         get_derived_attributes(default_asset, defaults);
134 //      to_tracks = defaults->get("RENDER_EFFECT_TO_TRACKS", 1);
135         load_mode = defaults->get("RENDER_EFFECT_LOADMODE", LOAD_PASTE);
136         strategy = defaults->get("RENDER_EFFECT_STRATEGY", SINGLE_PASS);
138 // get plugin information
139         int need_plugin;
140         if(!strlen(title)) 
141                 need_plugin = 1; 
142         else 
143                 need_plugin = 0;
145 // generate a list of plugins for the window
146         if(need_plugin)
147         {
148                 mwindow->create_plugindb(default_asset->audio_data, 
149                         default_asset->video_data, 
150                         -1, 
151                         0,
152                         0,
153                         local_plugindb);
155                 for(int i = 0; i < local_plugindb.total; i++)
156                 {
157                         plugin_list.append(new BC_ListBoxItem(_(local_plugindb.values[i]->title)));
158                 }
159         }
161 // find out which effect to run and get output file
162         int plugin_number, format_error = 0;
164         do
165         {
166                 {
167                         MenuEffectWindow window(mwindow, 
168                                 this, 
169                                 need_plugin ? &plugin_list : 0, 
170                                 default_asset);
171                         window.create_objects();
172                         result = window.run_window();
173                         plugin_number = window.result;
174                 }
176                 if(!result)
177                 {
178                         FormatCheck format_check(default_asset);
179                         format_error = format_check.check_format();
180                 }
181         }while(format_error && !result);
183 // save defaults
184         save_derived_attributes(default_asset, defaults);
185         defaults->update("RENDER_EFFECT_LOADMODE", load_mode);
186         defaults->update("RENDER_EFFECT_STRATEGY", strategy);
187         mwindow->save_defaults();
189 // get plugin server to use and delete the plugin list
190         PluginServer *plugin_server = 0;
191         PluginServer *plugin = 0;
192         if(need_plugin)
193         {
194                 plugin_list.remove_all_objects();
195                 if(plugin_number > -1)
196                 {
197                         plugin_server = local_plugindb.values[plugin_number];
198                         strcpy(title, plugin_server->title);
199                 }
200         }
201         else
202         {
203                 for(int i = 0; i < plugindb->total && !plugin_server; i++)
204                 {
205                         if(!strcmp(plugindb->values[i]->title, title))
206                         {
207                                 plugin_server = plugindb->values[i];
208                                 plugin_number = i;
209                         }
210                 }
211         }
213 // Update the  most recently used effects and copy the plugin server.
214         if(plugin_server)
215         {
216                 plugin = new PluginServer(*plugin_server);
217                 fix_menu(title);
218         }
220         if(!result && !strlen(default_asset->path))
221         {
222                 result = 1;        // no output path given
223                 ErrorBox error(PROGRAM_NAME ": Error");
224                 error.create_objects(_("No output file specified."));
225                 error.run_window();
226         }
228         if(!result && plugin_number < 0)
229         {
230                 result = 1;        // no output path given
231                 ErrorBox error(PROGRAM_NAME ": Error");
232                 error.create_objects(_("No effect selected."));
233                 error.run_window();
234         }
236 // Configuration for realtime plugins.
237         KeyFrame plugin_data;        
239 // get selection to render
240 // Range
241         double total_start, total_end;
243         total_start = mwindow->edl->local_session->get_selectionstart();
246         if(mwindow->edl->local_session->get_selectionend() == 
247                 mwindow->edl->local_session->get_selectionstart())
248                 total_end = mwindow->edl->tracks->total_playable_length();
249         else
250                 total_end = mwindow->edl->local_session->get_selectionend();
254 // get native units for range
255         total_start = to_units(total_start, 0);
256         total_end = to_units(total_end, 1);
260 // Trick boundaries in case of a non-realtime synthesis plugin
261         if(plugin && 
262                 !plugin->realtime && 
263                 total_end == total_start) total_end = total_start + 1;
265 // Units are now in the track's units.
266         int64_t total_length = (int64_t)total_end - (int64_t)total_start;
267 // length of output file
268         int64_t output_start, output_end;        
270         if(!result && total_length <= 0)
271         {
272                 result = 1;        // no output path given
273                 ErrorBox error(PROGRAM_NAME ": Error");
274                 error.create_objects(_("No selected range to process."));
275                 error.run_window();
276         }
278 // ========================= get keyframe from user
279         if(!result)
280         {
281 // ========================= realtime plugin 
282 // no get_parameters
283                 if(plugin->realtime)
284                 {
285 // Open a prompt GUI
286                         MenuEffectPrompt prompt(mwindow);
287                         prompt.create_objects();
288                         char title[BCTEXTLEN];
289                         sprintf(title, PROGRAM_NAME ": %s", plugin->title);
291 // Open the plugin GUI
292                         plugin->set_mwindow(mwindow);
293                         plugin->set_keyframe(&plugin_data);
294                         plugin->set_prompt(&prompt);
295                         plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0, -1);
296 // Must set parameters since there is no plugin object to draw from.
297                         plugin->get_parameters((int64_t)total_start,
298                                 (int64_t)total_end,
299                                 1);
300                         plugin->show_gui();
302 // wait for user input
303                         result = prompt.run_window();
305 // Close plugin.
306                         plugin->save_data(&plugin_data);
307                         delete plugin;
308                         default_asset->sample_rate = mwindow->edl->session->sample_rate;
309                         default_asset->frame_rate = mwindow->edl->session->frame_rate;
310                         realtime = 1;
311                 }
312                 else
313 // ============================non realtime plugin 
314                 {
315                         plugin->set_mwindow(mwindow);
316                         plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0, -1);
317                         result = plugin->get_parameters((int64_t)total_start, 
318                                 (int64_t)total_end, 
319                                 get_recordable_tracks(default_asset));
320 // some plugins can change the sample rate and the frame rate
323                         if(!result)
324                         {
325                                 default_asset->sample_rate = plugin->get_samplerate();
326                                 default_asset->frame_rate = plugin->get_framerate();
327                         }
328                         delete plugin;
329                         realtime = 0;
330                 }
332 // Should take from first recordable track
333                 default_asset->width = mwindow->edl->session->output_w;
334                 default_asset->height = mwindow->edl->session->output_h;
335         }
337 // Process the total length in fragments
338         ArrayList<MenuEffectPacket*> packets;
339         if(!result)
340         {
341                 Label *current_label = mwindow->edl->labels->first;
342                 mwindow->stop_brender();
344                 int current_number;
345                 int number_start;
346                 int total_digits;
347                 Render::get_starting_number(default_asset->path, 
348                         current_number,
349                         number_start, 
350                         total_digits);
354 // Construct all packets for single overwrite confirmation
355                 for(int64_t fragment_start = (int64_t)total_start, fragment_end;
356                         fragment_start < (int64_t)total_end;
357                         fragment_start = fragment_end)
358                 {
359 // Get fragment end
360                         if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
361                         {
362                                 while(current_label  &&
363                                         to_units(current_label->position, 0) <= fragment_start)
364                                         current_label = current_label->next;
365                                 if(!current_label)
366                                         fragment_end = (int64_t)total_end;
367                                 else
368                                         fragment_end = to_units(current_label->position, 0);
369                         }
370                         else
371                         {
372                                 fragment_end = (int64_t)total_end;
373                         }
375 // Get path
376                         char path[BCTEXTLEN];
377                         if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM) 
378                                 Render::create_filename(path, 
379                                         default_asset->path, 
380                                         current_number,
381                                         total_digits,
382                                         number_start);
383                         else
384                                 strcpy(path, default_asset->path);
385                         current_number++;
387                         MenuEffectPacket *packet = new MenuEffectPacket(path, 
388                                 fragment_start,
389                                 fragment_end);
390                         packets.append(packet);
391                 }
394 // Test existence of files
395                 ArrayList<char*> paths;
396                 for(int i = 0; i < packets.total; i++)
397                 {
398                         paths.append(packets.values[i]->path);
399                 }
400                 result = ConfirmSave::test_files(mwindow, &paths);
401                 paths.remove_all();
402         }
406         for(int current_packet = 0; 
407                 current_packet < packets.total && !result; 
408                 current_packet++)
409         {
410                 Asset *asset = new Asset(*default_asset);
411                 MenuEffectPacket *packet = packets.values[current_packet];
412                 int64_t fragment_start = packet->start;
413                 int64_t fragment_end = packet->end;
414                 strcpy(asset->path, packet->path);
416                 assets.append(asset);
417                 File *file = new File;
419 // Open the output file after getting the information because the sample rate
420 // is needed here.
421                 if(!result)
422                 {
423 // open output file in write mode
424                         file->set_processors(mwindow->preferences->processors);
425                         if(file->open_file(mwindow->preferences, 
426                                 asset, 
427                                 0, 
428                                 1, 
429                                 mwindow->edl->session->sample_rate, 
430                                 mwindow->edl->session->frame_rate))
431                         {
432 // open failed
433                                 sprintf(string, _("Couldn't open %s"), asset->path);
434                                 ErrorBox error(PROGRAM_NAME ": Error");
435                                 error.create_objects(string);
436                                 error.run_window();
437                                 result = 1;
438                         }
439                         else
440                         {
441                                 mwindow->sighandler->push_file(file);
442                                 IndexFile::delete_index(mwindow->preferences, asset);
443                         }
444                 }
446 // run plugins
447                 if(!result)
448                 {
449 // position file
450                         output_start = 0;
452                         PluginArray *plugin_array;
453                         plugin_array = create_plugin_array();
455                         plugin_array->start_plugins(mwindow, 
456                                 mwindow->edl, 
457                                 plugin_server, 
458                                 &plugin_data,
459                                 fragment_start,
460                                 fragment_end,
461                                 file);
462                         plugin_array->run_plugins();
464                         plugin_array->stop_plugins();
465                         mwindow->sighandler->pull_file(file);
466                         file->close_file();
467                         asset->audio_length = file->asset->audio_length;
468                         asset->video_length = file->asset->video_length;
469                         delete plugin_array;
470                 }
472                 delete file;
473         }
475         packets.remove_all_objects();
477 // paste output to tracks
478         if(!result && load_mode != LOAD_NOTHING)
479         {
480                 mwindow->gui->lock_window("MenuEffectThread::run");
482                 if(load_mode == LOAD_PASTE)
483                         mwindow->clear(0);
484                 mwindow->load_assets(&assets,
485                         -1,
486                         load_mode,
487                         0,
488                         0,
489                         mwindow->edl->session->labels_follow_edits, 
490                         mwindow->edl->session->plugins_follow_edits,
491                         0); // overwrite
494                 mwindow->save_backup();
495                 mwindow->undo->update_undo(title, LOAD_ALL);
499                 mwindow->restart_brender();
500                 mwindow->update_plugin_guis();
501                 mwindow->gui->update(1, 
502                         2,
503                         1,
504                         1,
505                         1,
506                         1,
507                         0);
508                 mwindow->sync_parameters(CHANGE_ALL);
509                 mwindow->gui->unlock_window();
510         }
512         for(int i = 0; i < assets.total; i++)
513                 Garbage::delete_object(assets.values[i]);
514         assets.remove_all();
515         Garbage::delete_object(default_asset);
521 MenuEffectItem::MenuEffectItem(MenuEffects *menueffect, char *string)
522  : BC_MenuItem(string)
524         this->menueffect = menueffect; 
526 int MenuEffectItem::handle_event()
528         menueffect->thread->set_title(get_text());
529         menueffect->thread->start();
543 MenuEffectWindow::MenuEffectWindow(MWindow *mwindow, 
544         MenuEffectThread *menueffects, 
545         ArrayList<BC_ListBoxItem*> *plugin_list, 
546         Asset *asset)
547  : BC_Window(PROGRAM_NAME ": Render effect", 
548                 mwindow->gui->get_abs_cursor_x(1),
549                 mwindow->gui->get_abs_cursor_y(1) - mwindow->session->menueffect_h / 2,
550                 mwindow->session->menueffect_w, 
551                 mwindow->session->menueffect_h, 
552                 580,
553                 350,
554                 1,
555                 0,
556                 1)
558         this->menueffects = menueffects; 
559         this->plugin_list = plugin_list; 
560         this->asset = asset;
561         this->mwindow = mwindow;
564 MenuEffectWindow::~MenuEffectWindow()
566         delete format_tools;
571 int MenuEffectWindow::create_objects()
573         int x, y;
574         result = -1;
575         mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
577 // only add the list if needed
578         if(plugin_list)
579         {
580                 add_subwindow(list_title = new BC_Title(mwindow->theme->menueffect_list_x, 
581                         mwindow->theme->menueffect_list_y, 
582                         _("Select an effect")));
583                 add_subwindow(list = new MenuEffectWindowList(this, 
584                         mwindow->theme->menueffect_list_x, 
585                         mwindow->theme->menueffect_list_y + list_title->get_h() + 5, 
586                         mwindow->theme->menueffect_list_w,
587                         mwindow->theme->menueffect_list_h - list_title->get_h() - 5,
588                         plugin_list));
589         }
591         add_subwindow(file_title = new BC_Title(mwindow->theme->menueffect_file_x, 
592                 mwindow->theme->menueffect_file_y, 
593                 (char*)((menueffects->strategy == FILE_PER_LABEL  || menueffects->strategy == FILE_PER_LABEL_FARM) ? 
594                         _("Select the first file to render to:") : 
595                         _("Select a file to render to:"))));
597         x = mwindow->theme->menueffect_tools_x;
598         y = mwindow->theme->menueffect_tools_y;
599         format_tools = new FormatTools(mwindow,
600                                         this, 
601                                         asset);
602         format_tools->create_objects(x, 
603                                         y, 
604                                         asset->audio_data, 
605                                         asset->video_data, 
606                                         0, 
607                                         0, 
608                                         0,
609                                         1,
610                                         0,
611                                         0,
612                                         &menueffects->strategy,
613                                         0);
615         loadmode = new LoadMode(mwindow, 
616                 this, 
617                 x, 
618                 y, 
619                 &menueffects->load_mode, 
620                 1);
621         loadmode->create_objects();
623         add_subwindow(new MenuEffectWindowOK(this));
624         add_subwindow(new MenuEffectWindowCancel(this));
625         show_window();
626         flush();
627         return 0;
630 int MenuEffectWindow::resize_event(int w, int h)
632         mwindow->session->menueffect_w = w;
633         mwindow->session->menueffect_h = h;
634         mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
636         if(plugin_list)
637         {
638                 list_title->reposition_window(mwindow->theme->menueffect_list_x, 
639                         mwindow->theme->menueffect_list_y);
640                 list->reposition_window(mwindow->theme->menueffect_list_x, 
641                         mwindow->theme->menueffect_list_y + list_title->get_h() + 5, 
642                         mwindow->theme->menueffect_list_w,
643                         mwindow->theme->menueffect_list_h - list_title->get_h() - 5);
644         }
646         file_title->reposition_window(mwindow->theme->menueffect_file_x, 
647                 mwindow->theme->menueffect_file_y);
648         int x = mwindow->theme->menueffect_tools_x;
649         int y = mwindow->theme->menueffect_tools_y;
650         format_tools->reposition_window(x, y);
651         loadmode->reposition_window(x, y);
656 MenuEffectWindowOK::MenuEffectWindowOK(MenuEffectWindow *window)
657  : BC_OKButton(window)
659         this->window = window; 
662 int MenuEffectWindowOK::handle_event() 
664         if(window->plugin_list) 
665                 window->result = window->list->get_selection_number(0, 0); 
666         
667         window->set_done(0); 
670 int MenuEffectWindowOK::keypress_event() 
672         if(get_keypress() == RETURN) 
673         { 
674                 handle_event(); 
675                 return 1; 
676         }
677         return 0;
680 MenuEffectWindowCancel::MenuEffectWindowCancel(MenuEffectWindow *window)
681  : BC_CancelButton(window)
683         this->window = window; 
686 int MenuEffectWindowCancel::handle_event() 
688         window->set_done(1); 
691 int MenuEffectWindowCancel::keypress_event() 
693         if(get_keypress() == ESC) 
694         { 
695                 handle_event(); 
696                 return 1; 
697         }
698         return 0;
701 MenuEffectWindowList::MenuEffectWindowList(MenuEffectWindow *window, 
702         int x, 
703         int y, 
704         int w, 
705         int h, 
706         ArrayList<BC_ListBoxItem*> *plugin_list)
707  : BC_ListBox(x, 
708                 y, 
709                 w, 
710                 h, 
711                 LISTBOX_TEXT, 
712                 plugin_list)
714         this->window = window; 
717 int MenuEffectWindowList::handle_event() 
719         window->result = get_selection_number(0, 0);
720         window->set_done(0); 
723 #define PROMPT_TEXT _("Set up effect panel and hit \"OK\"")
725 MenuEffectPrompt::MenuEffectPrompt(MWindow *mwindow)
726  : BC_Window(PROGRAM_NAME ": Effect Prompt", 
727                 mwindow->gui->get_abs_cursor_x(1) - 260 / 2,
728                 mwindow->gui->get_abs_cursor_y(1) - 300,
729                 MenuEffectPrompt::calculate_w(mwindow->gui), 
730                 MenuEffectPrompt::calculate_h(mwindow->gui), 
731                 MenuEffectPrompt::calculate_w(mwindow->gui),
732                 MenuEffectPrompt::calculate_h(mwindow->gui),
733                 0,
734                 0,
735                 1)
739 int MenuEffectPrompt::calculate_w(BC_WindowBase *gui)
741         int w = BC_Title::calculate_w(gui, PROMPT_TEXT) + 10;
742         w = MAX(w, BC_OKButton::calculate_w() + BC_CancelButton::calculate_w() + 30);
743         return w;
746 int MenuEffectPrompt::calculate_h(BC_WindowBase *gui)
748         int h = BC_Title::calculate_h(gui, PROMPT_TEXT);
749         h += BC_OKButton::calculate_h() + 30;
750         return h;
754 int MenuEffectPrompt::create_objects()
756         int x = 10, y = 10;
757         BC_Title *title;
758         add_subwindow(title = new BC_Title(x, y, PROMPT_TEXT));
759         add_subwindow(new BC_OKButton(this));
760         add_subwindow(new BC_CancelButton(this));
761         show_window();
762         raise_window();
763         flush();
764         return 0;