4 * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "awindowgui.h"
26 #include "confirmsave.h"
29 #include "edlsession.h"
32 #include "formatcheck.h"
33 #include "indexfile.h"
39 #include "localsession.h"
41 #include "mainsession.h"
44 #include "mwindowgui.h"
45 #include "menueffects.h"
46 #include "playbackengine.h"
47 #include "pluginarray.h"
48 #include "pluginserver.h"
49 #include "preferences.h"
51 #include "sighandler.h"
57 MenuEffects::MenuEffects(MWindow *mwindow)
58 : BC_MenuItem(_("Render effect..."))
60 this->mwindow = mwindow;
63 MenuEffects::~MenuEffects()
68 int MenuEffects::handle_event()
70 thread->set_title("");
79 MenuEffectPacket::MenuEffectPacket(char *path, int64_t start, int64_t end)
83 strcpy(this->path, path);
86 MenuEffectPacket::~MenuEffectPacket()
95 MenuEffectThread::MenuEffectThread(MWindow *mwindow, MenuEffects *menu_item)
97 this->mwindow = mwindow;
98 this->menu_item = menu_item;
100 dead_plugins = new ArrayList<PluginServer*>;
103 MenuEffectThread::~MenuEffectThread()
112 int MenuEffectThread::set_title(const char *title)
114 strcpy(this->title, title);
118 // for recent effect menu items and running new effects
119 // prompts for an effect if title is blank
120 void MenuEffectThread::run()
122 for(int i = 0; i < dead_plugins->size(); i++)
124 delete dead_plugins->get(i);
126 dead_plugins->remove_all();
131 // get stuff from main window
132 ArrayList<PluginServer*> *plugindb = mwindow->plugindb;
133 BC_Hash *defaults = mwindow->defaults;
134 ArrayList<BC_ListBoxItem*> plugin_list;
135 ArrayList<PluginServer*> local_plugindb;
138 // Default configuration
139 Asset *default_asset = new Asset;
141 ArrayList<Indexable*> assets;
144 // check for recordable tracks
145 if(!get_recordable_tracks(default_asset))
147 sprintf(string, _("No recordable tracks specified."));
148 ErrorBox error(_(PROGRAM_NAME ": Error"));
149 error.create_objects(string);
151 default_asset->Garbage::remove_user();
158 sprintf(string, _("No plugins available."));
159 ErrorBox error(_(PROGRAM_NAME ": Error"));
160 error.create_objects(string);
162 default_asset->Garbage::remove_user();
167 // get default attributes for output file
168 // used after completion
169 get_derived_attributes(default_asset, defaults);
170 // to_tracks = defaults->get("RENDER_EFFECT_TO_TRACKS", 1);
171 load_mode = defaults->get("RENDER_EFFECT_LOADMODE", LOADMODE_PASTE);
172 use_labels = defaults->get("RENDER_FILE_PER_LABEL", 0);
174 // get plugin information
175 int need_plugin = !strlen(title) ? 1 : 0;
176 // generate a list of plugins for the window
178 mwindow->search_plugindb(default_asset->audio_data,
179 default_asset->video_data, -1, 0, 0, local_plugindb);
180 for(int i = 0; i < local_plugindb.total; i++) {
181 plugin_list.append(new BC_ListBoxItem(_(local_plugindb.values[i]->title)));
185 // find out which effect to run and get output file
186 int plugin_number, format_error = 0;
191 MenuEffectWindow window(mwindow,
193 need_plugin ? &plugin_list : 0,
195 window.create_objects();
196 result = window.run_window();
197 plugin_number = window.result;
202 FormatCheck format_check(default_asset);
203 format_error = format_check.check_format();
205 }while(format_error && !result);
208 save_derived_attributes(default_asset, defaults);
209 defaults->update("RENDER_EFFECT_LOADMODE", load_mode);
210 defaults->update("RENDER_EFFECT_FILE_PER_LABEL", use_labels);
211 mwindow->save_defaults();
213 // get plugin server to use and delete the plugin list
214 PluginServer *plugin_server = 0;
215 PluginServer *plugin = 0;
218 plugin_list.remove_all_objects();
219 if(plugin_number > -1)
221 plugin_server = local_plugindb.values[plugin_number];
222 strcpy(title, plugin_server->title);
227 for(int i = 0; i < plugindb->total && !plugin_server; i++)
229 if(!strcmp(plugindb->values[i]->title, title))
231 plugin_server = plugindb->values[i];
237 // Update the most recently used effects and copy the plugin server.
240 plugin = new PluginServer(*plugin_server);
244 if(!result && !strlen(default_asset->path))
246 result = 1; // no output path given
247 ErrorBox error(_(PROGRAM_NAME ": Error"));
248 error.create_objects(_("No output file specified."));
252 if(!result && plugin_number < 0)
254 result = 1; // no output path given
255 ErrorBox error(_(PROGRAM_NAME ": Error"));
256 error.create_objects(_("No effect selected."));
260 // Configuration for realtime plugins.
261 KeyFrame plugin_data;
263 // get selection to render
265 double total_start, total_end;
267 total_start = mwindow->edl->local_session->get_selectionstart();
270 if(mwindow->edl->local_session->get_selectionend() ==
271 mwindow->edl->local_session->get_selectionstart())
272 total_end = mwindow->edl->tracks->total_length();
274 total_end = mwindow->edl->local_session->get_selectionend();
278 // get native units for range
279 total_start = to_units(total_start, 0);
280 total_end = to_units(total_end, 1);
284 // Trick boundaries in case of a non-realtime synthesis plugin
287 total_end == total_start) total_end = total_start + 1;
289 // Units are now in the track's units.
290 int64_t total_length = (int64_t)total_end - (int64_t)total_start;
291 // length of output file
293 if(!result && total_length <= 0)
295 result = 1; // no output path given
296 ErrorBox error(_(PROGRAM_NAME ": Error"));
297 error.create_objects(_("No selected range to process."));
301 // ========================= get keyframe from user
304 // ========================= realtime plugin
309 MenuEffectPrompt prompt(mwindow);
310 prompt.create_objects();
311 char title[BCTEXTLEN];
312 sprintf(title, _(PROGRAM_NAME ": %s"), plugin->title);
314 // Open the plugin GUI
315 plugin->set_mwindow(mwindow);
316 plugin->set_keyframe(&plugin_data);
317 plugin->set_prompt(&prompt);
318 plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
319 // Must set parameters since there is no plugin object to draw from.
320 plugin->get_parameters((int64_t)total_start,
325 // wait for user input
326 result = prompt.run_window();
329 plugin->save_data(&plugin_data);
332 // Can't delete here.
333 dead_plugins->append(plugin);
334 default_asset->sample_rate = mwindow->edl->session->sample_rate;
335 default_asset->frame_rate = mwindow->edl->session->frame_rate;
339 // ============================non realtime plugin
341 plugin->set_mwindow(mwindow);
342 plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
343 result = plugin->get_parameters((int64_t)total_start,
345 get_recordable_tracks(default_asset));
346 // some plugins can change the sample rate and the frame rate
351 default_asset->sample_rate = plugin->get_samplerate();
352 default_asset->frame_rate = plugin->get_framerate();
358 // Should take from first recordable track
359 default_asset->width = mwindow->edl->session->output_w;
360 default_asset->height = mwindow->edl->session->output_h;
363 int range = File::is_image_render(default_asset->format) ?
364 RANGE_1FRAME : RANGE_SELECTION;
365 int strategy = Render::get_strategy(mwindow->preferences->use_renderfarm, use_labels, range);
366 // Process the total length in fragments
367 ArrayList<MenuEffectPacket*> packets;
370 Label *current_label = mwindow->edl->labels->first;
371 mwindow->stop_brender();
376 Render::get_starting_number(default_asset->path,
383 // Construct all packets for single overwrite confirmation
384 for(int64_t fragment_start = (int64_t)total_start, fragment_end;
385 fragment_start < (int64_t)total_end;
386 fragment_start = fragment_end)
389 if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
391 while(current_label &&
392 to_units(current_label->position, 0) <= fragment_start)
393 current_label = current_label->next;
395 fragment_end = (int64_t)total_end;
397 fragment_end = to_units(current_label->position, 0);
401 fragment_end = (int64_t)total_end;
405 char path[BCTEXTLEN];
406 if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
407 Render::create_filename(path,
413 strcpy(path, default_asset->path);
416 MenuEffectPacket *packet = new MenuEffectPacket(path,
419 packets.append(packet);
423 // Test existence of files
424 ArrayList<char*> paths;
425 for(int i = 0; i < packets.total; i++)
427 paths.append(packets.values[i]->path);
429 result = ConfirmSave::test_files(mwindow, &paths);
435 for(int current_packet = 0;
436 current_packet < packets.total && !result;
439 Asset *asset = new Asset(*default_asset);
440 MenuEffectPacket *packet = packets.values[current_packet];
441 int64_t fragment_start = packet->start;
442 int64_t fragment_end = packet->end;
443 strcpy(asset->path, packet->path);
445 assets.append(asset);
446 File *file = new File;
448 // Open the output file after getting the information because the sample rate
452 // open output file in write mode
453 file->set_processors(mwindow->preferences->processors);
454 if(file->open_file(mwindow->preferences,
460 sprintf(string, _("Couldn't open %s"), asset->path);
461 ErrorBox error(_(PROGRAM_NAME ": Error"));
462 error.create_objects(string);
468 mwindow->sighandler->push_file(file);
469 IndexFile::delete_index(mwindow->preferences,
478 PluginArray *plugin_array;
479 plugin_array = create_plugin_array();
481 plugin_array->start_plugins(mwindow,
488 plugin_array->run_plugins();
490 plugin_array->stop_plugins();
491 mwindow->sighandler->pull_file(file);
493 asset->audio_length = file->asset->audio_length;
494 asset->video_length = file->asset->video_length;
501 packets.remove_all_objects();
503 // paste output to tracks
504 if(!result && load_mode != LOADMODE_NOTHING)
506 mwindow->gui->lock_window("MenuEffectThread::run");
508 mwindow->undo->update_undo_before("", 0);
509 if(load_mode == LOADMODE_PASTE)
512 mwindow->load_assets(&assets, -1, load_mode, 0, 0,
513 mwindow->edl->session->labels_follow_edits,
514 mwindow->edl->session->plugins_follow_edits,
515 mwindow->edl->session->autos_follow_edits,
518 mwindow->save_backup();
519 mwindow->undo->update_undo_after(title, LOAD_ALL);
521 mwindow->restart_brender();
522 mwindow->update_plugin_guis();
523 mwindow->gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
524 mwindow->sync_parameters(CHANGE_ALL);
525 mwindow->gui->unlock_window();
527 mwindow->awindow->gui->async_update_assets();
530 for(int i = 0; i < assets.total; i++)
531 assets.values[i]->Garbage::remove_user();
534 default_asset->Garbage::remove_user();
543 MenuEffectItem::MenuEffectItem(MenuEffects *menueffect, const char *string)
544 : BC_MenuItem(string)
546 this->menueffect = menueffect;
548 int MenuEffectItem::handle_event()
550 menueffect->thread->set_title(get_text());
551 menueffect->thread->start();
566 MenuEffectWindow::MenuEffectWindow(MWindow *mwindow,
567 MenuEffectThread *menueffects,
568 ArrayList<BC_ListBoxItem*> *plugin_list,
570 : BC_Window(_(PROGRAM_NAME ": Render effect"),
571 mwindow->gui->get_abs_cursor_x(1),
572 mwindow->gui->get_abs_cursor_y(1) - mwindow->session->menueffect_h / 2,
573 mwindow->session->menueffect_w,
574 mwindow->session->menueffect_h,
575 xS(580), yS(350), 1, 0, 1)
577 this->menueffects = menueffects;
578 this->plugin_list = plugin_list;
580 this->mwindow = mwindow;
584 // *** CONTEXT_HELP ***
585 context_help_set_keyword("Rendered Effects");
588 MenuEffectWindow::~MenuEffectWindow()
590 lock_window("MenuEffectWindow::~MenuEffectWindow");
597 void MenuEffectWindow::create_objects()
601 mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
603 lock_window("MenuEffectWindow::create_objects");
604 // only add the list if needed
607 add_subwindow(list_title = new BC_Title(mwindow->theme->menueffect_list_x,
608 mwindow->theme->menueffect_list_y,
609 _("Select an effect")));
611 add_subwindow(list = new MenuEffectWindowList(this,
612 mwindow->theme->menueffect_list_x,
613 mwindow->theme->menueffect_list_y + list_title->get_h() + ys5,
614 mwindow->theme->menueffect_list_w,
615 mwindow->theme->menueffect_list_h - list_title->get_h() - ys5,
619 add_subwindow(file_title = new BC_Title(
620 mwindow->theme->menueffect_file_x,
621 mwindow->theme->menueffect_file_y,
622 (char*)(menueffects->use_labels ?
623 _("Select the first file to render to:") :
624 _("Select a file to render to:"))));
626 x = mwindow->theme->menueffect_tools_x;
627 y = mwindow->theme->menueffect_tools_y;
628 format_tools = new FormatTools(mwindow,
631 format_tools->create_objects(x, y, asset->audio_data, asset->video_data,
632 0, 0, 0, 1, 0, 0, &menueffects->use_labels, 0);
634 loadmode = new LoadMode(mwindow, this, x, y, &menueffects->load_mode);
635 loadmode->create_objects();
637 add_subwindow(new MenuEffectWindowOK(this));
638 add_subwindow(new MenuEffectWindowCancel(this));
643 int MenuEffectWindow::resize_event(int w, int h)
645 mwindow->session->menueffect_w = w;
646 mwindow->session->menueffect_h = h;
647 mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
651 list_title->reposition_window(mwindow->theme->menueffect_list_x,
652 mwindow->theme->menueffect_list_y);
654 list->reposition_window(mwindow->theme->menueffect_list_x,
655 mwindow->theme->menueffect_list_y + list_title->get_h() + ys5,
656 mwindow->theme->menueffect_list_w,
657 mwindow->theme->menueffect_list_h - list_title->get_h() - ys5);
660 if(file_title) file_title->reposition_window(mwindow->theme->menueffect_file_x,
661 mwindow->theme->menueffect_file_y);
662 int x = mwindow->theme->menueffect_tools_x;
663 int y = mwindow->theme->menueffect_tools_y;
664 if(format_tools) format_tools->reposition_window(x, y);
665 if(loadmode) loadmode->reposition_window(x, y);
671 MenuEffectWindowOK::MenuEffectWindowOK(MenuEffectWindow *window)
672 : BC_OKButton(window)
674 this->window = window;
677 int MenuEffectWindowOK::handle_event()
679 if(window->plugin_list)
680 window->result = window->list->get_selection_number(0, 0);
686 int MenuEffectWindowOK::keypress_event()
688 if(get_keypress() == RETURN)
693 return context_help_check_and_show();
696 MenuEffectWindowCancel::MenuEffectWindowCancel(MenuEffectWindow *window)
697 : BC_CancelButton(window)
699 this->window = window;
702 int MenuEffectWindowCancel::handle_event()
708 int MenuEffectWindowCancel::keypress_event()
710 if(get_keypress() == ESC)
715 return context_help_check_and_show();
718 MenuEffectWindowList::MenuEffectWindowList(MenuEffectWindow *window,
723 ArrayList<BC_ListBoxItem*> *plugin_list)
724 : BC_ListBox(x, y, w, h, LISTBOX_TEXT, plugin_list)
726 this->window = window;
729 int MenuEffectWindowList::handle_event()
731 window->result = get_selection_number(0, 0);
736 // *** CONTEXT_HELP ***
737 int MenuEffectWindowList::keypress_event()
740 char title[BCTEXTLEN];
742 // printf("MenuEffectWindowList::keypress_event: %d\n", get_keypress());
744 // If not our context help keystroke, redispatch it
745 // to the event handler of the base class
746 if (get_keypress() != 'h' || ! alt_down() ||
747 ! is_tooltip_event_win() || ! cursor_inside())
748 return BC_ListBox::keypress_event();
750 // Try to show help for the plugin currently under mouse
752 item = get_highlighted_item();
753 if (item >= 0 && item < window->plugin_list->total)
754 strcpy(title, window->plugin_list->values[item]->get_text());
756 // If some plugin is highlighted, show its help
757 // Otherwise show more general help
759 if (! strcmp(title, "Overlay")) {
760 // "Overlay" plugin title is ambiguous
761 if (window->asset->audio_data)
762 strcat(title, " \\(Audio\\)");
763 if (window->asset->video_data)
764 strcat(title, " \\(Video\\)");
766 if (! strncmp(title, "F_", 2)) {
767 // FFmpeg plugins can be audio or video
768 if (window->asset->audio_data)
769 strcpy(title, "FFmpeg Audio Plugins");
770 if (window->asset->video_data)
771 strcpy(title, "FFmpeg Video Plugins");
773 context_help_show(title);
777 context_help_show("Rendered Effects");
780 context_help_show("Rendered Effects");
784 #define PROMPT_TEXT _("Set up effect panel and hit \"OK\"")
785 #define MEP_W xS(260)
786 #define MEP_H yS(100)
788 MenuEffectPrompt::MenuEffectPrompt(MWindow *mwindow)
789 : BC_Window(_(PROGRAM_NAME ": Effect Prompt"),
790 mwindow->gui->get_abs_cursor_x(1) - MEP_W/2,
791 mwindow->gui->get_abs_cursor_y(1) - MEP_H/2,
792 MenuEffectPrompt::calculate_w(mwindow->gui),
793 MenuEffectPrompt::calculate_h(mwindow->gui),
794 MenuEffectPrompt::calculate_w(mwindow->gui),
795 MenuEffectPrompt::calculate_h(mwindow->gui),
798 // *** CONTEXT_HELP ***
799 context_help_set_keyword("Rendered Effects");
802 int MenuEffectPrompt::calculate_w(BC_WindowBase *gui)
804 int w = BC_Title::calculate_w(gui, PROMPT_TEXT) + xS(10);
805 w = MAX(w, BC_OKButton::calculate_w() + BC_CancelButton::calculate_w() + xS(30));
809 int MenuEffectPrompt::calculate_h(BC_WindowBase *gui)
811 int h = BC_Title::calculate_h(gui, PROMPT_TEXT);
812 h += BC_OKButton::calculate_h() + yS(30);
817 void MenuEffectPrompt::create_objects()
819 lock_window("MenuEffectPrompt::create_objects");
820 int x = xS(10), y = yS(10);
822 add_subwindow(title = new BC_Title(x, y, PROMPT_TEXT));
823 add_subwindow(new BC_OKButton(this));
824 add_subwindow(new BC_CancelButton(this));