Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / menueffects.C
diff --git a/cinelerra-5.1/cinelerra/menueffects.C b/cinelerra-5.1/cinelerra/menueffects.C
new file mode 100644 (file)
index 0000000..6705e40
--- /dev/null
@@ -0,0 +1,814 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "asset.h"
+#include "awindow.h"
+#include "awindowgui.h"
+#include "clip.h"
+#include "confirmsave.h"
+#include "bchash.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "errorbox.h"
+#include "file.h"
+#include "formatcheck.h"
+#include "indexfile.h"
+#include "keyframe.h"
+#include "keys.h"
+#include "labels.h"
+#include "language.h"
+#include "loadmode.h"
+#include "localsession.h"
+#include "mainmenu.h"
+#include "mainsession.h"
+#include "mainundo.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "menueffects.h"
+#include "playbackengine.h"
+#include "pluginarray.h"
+#include "pluginserver.h"
+#include "preferences.h"
+#include "render.h"
+#include "sighandler.h"
+#include "theme.h"
+#include "tracks.h"
+
+
+
+MenuEffects::MenuEffects(MWindow *mwindow)
+ : BC_MenuItem(_("Render effect..."))
+{
+       this->mwindow = mwindow;
+}
+
+MenuEffects::~MenuEffects()
+{
+}
+
+
+int MenuEffects::handle_event()
+{
+       thread->set_title("");
+       thread->start();
+       return 1;
+}
+
+
+
+
+
+MenuEffectPacket::MenuEffectPacket(char *path, int64_t start, int64_t end)
+{
+       this->start = start;
+       this->end = end;
+       strcpy(this->path, path);
+}
+
+MenuEffectPacket::~MenuEffectPacket()
+{
+}
+
+
+
+
+
+
+MenuEffectThread::MenuEffectThread(MWindow *mwindow, MenuEffects *menu_item)
+{
+       this->mwindow = mwindow;
+       this->menu_item = menu_item;
+       title[0] = 0;
+       dead_plugins = new ArrayList<PluginServer*>;
+}
+
+MenuEffectThread::~MenuEffectThread()
+{
+       delete dead_plugins;
+}
+
+
+
+
+
+int MenuEffectThread::set_title(const char *title)
+{
+       strcpy(this->title, title);
+       return 0;
+}
+
+// for recent effect menu items and running new effects
+// prompts for an effect if title is blank
+void MenuEffectThread::run()
+{
+       for(int i = 0; i < dead_plugins->size(); i++)
+       {
+               delete dead_plugins->get(i);
+       }
+       dead_plugins->remove_all();
+
+
+
+
+// get stuff from main window
+       ArrayList<PluginServer*> *plugindb = mwindow->plugindb;
+       BC_Hash *defaults = mwindow->defaults;
+       ArrayList<BC_ListBoxItem*> plugin_list;
+       ArrayList<PluginServer*> local_plugindb;
+       char string[1024];
+       int result = 0;
+// Default configuration
+       Asset *default_asset = new Asset;
+// Output
+       ArrayList<Indexable*> assets;
+
+
+// check for recordable tracks
+       if(!get_recordable_tracks(default_asset))
+       {
+               sprintf(string, _("No recordable tracks specified."));
+               ErrorBox error(_(PROGRAM_NAME ": Error"));
+               error.create_objects(string);
+               error.run_window();
+               default_asset->Garbage::remove_user();
+               return;
+       }
+
+// check for plugins
+       if(!plugindb->total)
+       {
+               sprintf(string, _("No plugins available."));
+               ErrorBox error(_(PROGRAM_NAME ": Error"));
+               error.create_objects(string);
+               error.run_window();
+               default_asset->Garbage::remove_user();
+               return;
+       }
+
+
+// get default attributes for output file
+// used after completion
+       get_derived_attributes(default_asset, defaults);
+//     to_tracks = defaults->get("RENDER_EFFECT_TO_TRACKS", 1);
+       load_mode = defaults->get("RENDER_EFFECT_LOADMODE", LOADMODE_PASTE);
+       strategy = defaults->get("RENDER_EFFECT_STRATEGY", SINGLE_PASS);
+
+// get plugin information
+       int need_plugin;
+       if(!strlen(title))
+               need_plugin = 1;
+       else
+               need_plugin = 0;
+
+// generate a list of plugins for the window
+       if(need_plugin)
+       {
+               mwindow->search_plugindb(default_asset->audio_data,
+                       default_asset->video_data,
+                       -1,
+                       0,
+                       0,
+                       local_plugindb);
+
+               for(int i = 0; i < local_plugindb.total; i++)
+               {
+                       plugin_list.append(new BC_ListBoxItem(_(local_plugindb.values[i]->title)));
+               }
+       }
+
+// find out which effect to run and get output file
+       int plugin_number, format_error = 0;
+
+       do
+       {
+               {
+                       MenuEffectWindow window(mwindow,
+                               this,
+                               need_plugin ? &plugin_list : 0,
+                               default_asset);
+                       window.create_objects();
+                       result = window.run_window();
+                       plugin_number = window.result;
+               }
+
+               if(!result)
+               {
+                       FormatCheck format_check(default_asset);
+                       format_error = format_check.check_format();
+               }
+       }while(format_error && !result);
+
+// save defaults
+       save_derived_attributes(default_asset, defaults);
+       defaults->update("RENDER_EFFECT_LOADMODE", load_mode);
+       defaults->update("RENDER_EFFECT_STRATEGY", strategy);
+       mwindow->save_defaults();
+
+// get plugin server to use and delete the plugin list
+       PluginServer *plugin_server = 0;
+       PluginServer *plugin = 0;
+       if(need_plugin)
+       {
+               plugin_list.remove_all_objects();
+               if(plugin_number > -1)
+               {
+                       plugin_server = local_plugindb.values[plugin_number];
+                       strcpy(title, plugin_server->title);
+               }
+       }
+       else
+       {
+               for(int i = 0; i < plugindb->total && !plugin_server; i++)
+               {
+                       if(!strcmp(plugindb->values[i]->title, title))
+                       {
+                               plugin_server = plugindb->values[i];
+                               plugin_number = i;
+                       }
+               }
+       }
+
+// Update the  most recently used effects and copy the plugin server.
+       if(plugin_server)
+       {
+               plugin = new PluginServer(*plugin_server);
+               fix_menu(title);
+       }
+
+       if(!result && !strlen(default_asset->path))
+       {
+               result = 1;        // no output path given
+               ErrorBox error(_(PROGRAM_NAME ": Error"));
+               error.create_objects(_("No output file specified."));
+               error.run_window();
+       }
+
+       if(!result && plugin_number < 0)
+       {
+               result = 1;        // no output path given
+               ErrorBox error(_(PROGRAM_NAME ": Error"));
+               error.create_objects(_("No effect selected."));
+               error.run_window();
+       }
+
+// Configuration for realtime plugins.
+       KeyFrame plugin_data;
+
+// get selection to render
+// Range
+       double total_start, total_end;
+
+       total_start = mwindow->edl->local_session->get_selectionstart();
+
+
+       if(mwindow->edl->local_session->get_selectionend() ==
+               mwindow->edl->local_session->get_selectionstart())
+               total_end = mwindow->edl->tracks->total_playable_length();
+       else
+               total_end = mwindow->edl->local_session->get_selectionend();
+
+
+
+// get native units for range
+       total_start = to_units(total_start, 0);
+       total_end = to_units(total_end, 1);
+
+
+
+// Trick boundaries in case of a non-realtime synthesis plugin
+       if(plugin &&
+               !plugin->realtime &&
+               total_end == total_start) total_end = total_start + 1;
+
+// Units are now in the track's units.
+       int64_t total_length = (int64_t)total_end - (int64_t)total_start;
+// length of output file
+
+       if(!result && total_length <= 0)
+       {
+               result = 1;        // no output path given
+               ErrorBox error(_(PROGRAM_NAME ": Error"));
+               error.create_objects(_("No selected range to process."));
+               error.run_window();
+       }
+
+// ========================= get keyframe from user
+       if(!result)
+       {
+// ========================= realtime plugin
+// no get_parameters
+               if(plugin->realtime)
+               {
+// Open a prompt GUI
+                       MenuEffectPrompt prompt(mwindow);
+                       prompt.create_objects();
+                       char title[BCTEXTLEN];
+                       sprintf(title, _(PROGRAM_NAME ": %s"), plugin->title);
+
+// Open the plugin GUI
+                       plugin->set_mwindow(mwindow);
+                       plugin->set_keyframe(&plugin_data);
+                       plugin->set_prompt(&prompt);
+                       plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
+// Must set parameters since there is no plugin object to draw from.
+                       plugin->get_parameters((int64_t)total_start,
+                               (int64_t)total_end,
+                               1);
+                       plugin->show_gui();
+
+// wait for user input
+                       result = prompt.run_window();
+
+// Close plugin.
+                       plugin->save_data(&plugin_data);
+                       plugin->hide_gui();
+
+// Can't delete here.
+                       dead_plugins->append(plugin);
+                       default_asset->sample_rate = mwindow->edl->session->sample_rate;
+                       default_asset->frame_rate = mwindow->edl->session->frame_rate;
+                       realtime = 1;
+               }
+               else
+// ============================non realtime plugin
+               {
+                       plugin->set_mwindow(mwindow);
+                       plugin->open_plugin(0, mwindow->preferences, mwindow->edl, 0);
+                       result = plugin->get_parameters((int64_t)total_start,
+                               (int64_t)total_end,
+                               get_recordable_tracks(default_asset));
+// some plugins can change the sample rate and the frame rate
+
+
+                       if(!result)
+                       {
+                               default_asset->sample_rate = plugin->get_samplerate();
+                               default_asset->frame_rate = plugin->get_framerate();
+                       }
+                       delete plugin;
+                       realtime = 0;
+               }
+
+// Should take from first recordable track
+               default_asset->width = mwindow->edl->session->output_w;
+               default_asset->height = mwindow->edl->session->output_h;
+       }
+
+// Process the total length in fragments
+       ArrayList<MenuEffectPacket*> packets;
+       if(!result)
+       {
+               Label *current_label = mwindow->edl->labels->first;
+               mwindow->stop_brender();
+
+               int current_number;
+               int number_start;
+               int total_digits;
+               Render::get_starting_number(default_asset->path,
+                       current_number,
+                       number_start,
+                       total_digits);
+
+
+
+// Construct all packets for single overwrite confirmation
+               for(int64_t fragment_start = (int64_t)total_start, fragment_end;
+                       fragment_start < (int64_t)total_end;
+                       fragment_start = fragment_end)
+               {
+// Get fragment end
+                       if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
+                       {
+                               while(current_label  &&
+                                       to_units(current_label->position, 0) <= fragment_start)
+                                       current_label = current_label->next;
+                               if(!current_label)
+                                       fragment_end = (int64_t)total_end;
+                               else
+                                       fragment_end = to_units(current_label->position, 0);
+                       }
+                       else
+                       {
+                               fragment_end = (int64_t)total_end;
+                       }
+
+// Get path
+                       char path[BCTEXTLEN];
+                       if(strategy == FILE_PER_LABEL || strategy == FILE_PER_LABEL_FARM)
+                               Render::create_filename(path,
+                                       default_asset->path,
+                                       current_number,
+                                       total_digits,
+                                       number_start);
+                       else
+                               strcpy(path, default_asset->path);
+                       current_number++;
+
+                       MenuEffectPacket *packet = new MenuEffectPacket(path,
+                               fragment_start,
+                               fragment_end);
+                       packets.append(packet);
+               }
+
+
+// Test existence of files
+               ArrayList<char*> paths;
+               for(int i = 0; i < packets.total; i++)
+               {
+                       paths.append(packets.values[i]->path);
+               }
+               result = ConfirmSave::test_files(mwindow, &paths);
+               paths.remove_all();
+       }
+
+
+
+       for(int current_packet = 0;
+               current_packet < packets.total && !result;
+               current_packet++)
+       {
+               Asset *asset = new Asset(*default_asset);
+               MenuEffectPacket *packet = packets.values[current_packet];
+               int64_t fragment_start = packet->start;
+               int64_t fragment_end = packet->end;
+               strcpy(asset->path, packet->path);
+
+               assets.append(asset);
+               File *file = new File;
+
+// Open the output file after getting the information because the sample rate
+// is needed here.
+               if(!result)
+               {
+// open output file in write mode
+                       file->set_processors(mwindow->preferences->processors);
+                       if(file->open_file(mwindow->preferences,
+                               asset,
+                               0,
+                               1))
+                       {
+// open failed
+                               sprintf(string, _("Couldn't open %s"), asset->path);
+                               ErrorBox error(_(PROGRAM_NAME ": Error"));
+                               error.create_objects(string);
+                               error.run_window();
+                               result = 1;
+                       }
+                       else
+                       {
+                               mwindow->sighandler->push_file(file);
+                               IndexFile::delete_index(mwindow->preferences,
+                                       asset);
+                       }
+               }
+
+// run plugins
+               if(!result)
+               {
+// position file
+                       PluginArray *plugin_array;
+                       plugin_array = create_plugin_array();
+
+                       plugin_array->start_plugins(mwindow,
+                               mwindow->edl,
+                               plugin_server,
+                               &plugin_data,
+                               fragment_start,
+                               fragment_end,
+                               file);
+                       plugin_array->run_plugins();
+
+                       plugin_array->stop_plugins();
+                       mwindow->sighandler->pull_file(file);
+                       file->close_file();
+                       asset->audio_length = file->asset->audio_length;
+                       asset->video_length = file->asset->video_length;
+                       delete plugin_array;
+               }
+
+               delete file;
+       }
+
+       packets.remove_all_objects();
+
+// paste output to tracks
+       if(!result && load_mode != LOADMODE_NOTHING)
+       {
+               mwindow->gui->lock_window("MenuEffectThread::run");
+
+               mwindow->undo->update_undo_before("", 0);
+               if(load_mode == LOADMODE_PASTE)
+                       mwindow->clear(0);
+
+
+               mwindow->load_assets(&assets,
+                       -1,
+                       load_mode,
+                       0,
+                       0,
+                       mwindow->edl->session->labels_follow_edits,
+                       mwindow->edl->session->plugins_follow_edits,
+                       mwindow->edl->session->autos_follow_edits,
+                       0); // overwrite
+
+               mwindow->save_backup();
+               mwindow->undo->update_undo_after(title, LOAD_ALL);
+
+
+
+               mwindow->restart_brender();
+               mwindow->update_plugin_guis();
+               mwindow->gui->update(1,
+                       2,
+                       1,
+                       1,
+                       1,
+                       1,
+                       0);
+               mwindow->sync_parameters(CHANGE_ALL);
+               mwindow->gui->unlock_window();
+
+               mwindow->awindow->gui->async_update_assets();
+       }
+
+       for(int i = 0; i < assets.total; i++)
+               assets.values[i]->Garbage::remove_user();
+       assets.remove_all();
+
+       default_asset->Garbage::remove_user();
+}
+
+
+
+
+
+
+
+MenuEffectItem::MenuEffectItem(MenuEffects *menueffect, const char *string)
+ : BC_MenuItem(string)
+{
+       this->menueffect = menueffect;
+}
+int MenuEffectItem::handle_event()
+{
+       menueffect->thread->set_title(get_text());
+       menueffect->thread->start();
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+MenuEffectWindow::MenuEffectWindow(MWindow *mwindow,
+       MenuEffectThread *menueffects,
+       ArrayList<BC_ListBoxItem*> *plugin_list,
+       Asset *asset)
+ : BC_Window(_(PROGRAM_NAME ": Render effect"),
+               mwindow->gui->get_abs_cursor_x(1),
+               mwindow->gui->get_abs_cursor_y(1) - mwindow->session->menueffect_h / 2,
+               mwindow->session->menueffect_w,
+               mwindow->session->menueffect_h,
+               580,
+               350,
+               1,
+               0,
+               1)
+{
+       this->menueffects = menueffects;
+       this->plugin_list = plugin_list;
+       this->asset = asset;
+       this->mwindow = mwindow;
+       file_title = 0;
+       format_tools = 0;
+       loadmode = 0;
+}
+
+MenuEffectWindow::~MenuEffectWindow()
+{
+       lock_window("MenuEffectWindow::~MenuEffectWindow");
+       delete format_tools;
+       unlock_window();
+}
+
+
+
+void MenuEffectWindow::create_objects()
+{
+       int x, y;
+       result = -1;
+       mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
+
+       lock_window("MenuEffectWindow::create_objects");
+// only add the list if needed
+       if(plugin_list)
+       {
+               add_subwindow(list_title = new BC_Title(mwindow->theme->menueffect_list_x,
+                       mwindow->theme->menueffect_list_y,
+                       _("Select an effect")));
+               add_subwindow(list = new MenuEffectWindowList(this,
+                       mwindow->theme->menueffect_list_x,
+                       mwindow->theme->menueffect_list_y + list_title->get_h() + 5,
+                       mwindow->theme->menueffect_list_w,
+                       mwindow->theme->menueffect_list_h - list_title->get_h() - 5,
+                       plugin_list));
+       }
+
+       add_subwindow(file_title = new BC_Title(
+               mwindow->theme->menueffect_file_x,
+               mwindow->theme->menueffect_file_y,
+               (char*)((menueffects->strategy == FILE_PER_LABEL  || menueffects->strategy == FILE_PER_LABEL_FARM) ?
+                       _("Select the first file to render to:") :
+                       _("Select a file to render to:"))));
+
+       x = mwindow->theme->menueffect_tools_x;
+       y = mwindow->theme->menueffect_tools_y;
+       format_tools = new FormatTools(mwindow,
+                                       this,
+                                       asset);
+       format_tools->create_objects(x,
+                                       y,
+                                       asset->audio_data,
+                                       asset->video_data,
+                                       0,
+                                       0,
+                                       0,
+                                       1,
+                                       0,
+                                       0,
+                                       &menueffects->strategy,
+                                       0);
+
+       loadmode = new LoadMode(mwindow, this, x, y, &menueffects->load_mode, 1);
+       loadmode->create_objects();
+
+       add_subwindow(new MenuEffectWindowOK(this));
+       add_subwindow(new MenuEffectWindowCancel(this));
+       show_window();
+       unlock_window();
+}
+
+int MenuEffectWindow::resize_event(int w, int h)
+{
+       mwindow->session->menueffect_w = w;
+       mwindow->session->menueffect_h = h;
+       mwindow->theme->get_menueffect_sizes(plugin_list ? 1 : 0);
+
+       if(plugin_list)
+       {
+               list_title->reposition_window(mwindow->theme->menueffect_list_x,
+                       mwindow->theme->menueffect_list_y);
+               list->reposition_window(mwindow->theme->menueffect_list_x,
+                       mwindow->theme->menueffect_list_y + list_title->get_h() + 5,
+                       mwindow->theme->menueffect_list_w,
+                       mwindow->theme->menueffect_list_h - list_title->get_h() - 5);
+       }
+
+       if(file_title) file_title->reposition_window(mwindow->theme->menueffect_file_x,
+               mwindow->theme->menueffect_file_y);
+       int x = mwindow->theme->menueffect_tools_x;
+       int y = mwindow->theme->menueffect_tools_y;
+       if(format_tools) format_tools->reposition_window(x, y);
+       if(loadmode) loadmode->reposition_window(x, y);
+       return 0;
+}
+
+
+
+MenuEffectWindowOK::MenuEffectWindowOK(MenuEffectWindow *window)
+ : BC_OKButton(window)
+{
+       this->window = window;
+}
+
+int MenuEffectWindowOK::handle_event()
+{
+       if(window->plugin_list)
+               window->result = window->list->get_selection_number(0, 0);
+
+       window->set_done(0);
+       return 1;
+}
+
+int MenuEffectWindowOK::keypress_event()
+{
+       if(get_keypress() == RETURN)
+       {
+               handle_event();
+               return 1;
+       }
+       return 0;
+}
+
+MenuEffectWindowCancel::MenuEffectWindowCancel(MenuEffectWindow *window)
+ : BC_CancelButton(window)
+{
+       this->window = window;
+}
+
+int MenuEffectWindowCancel::handle_event()
+{
+       window->set_done(1);
+       return 1;
+}
+
+int MenuEffectWindowCancel::keypress_event()
+{
+       if(get_keypress() == ESC)
+       {
+               handle_event();
+               return 1;
+       }
+       return 0;
+}
+
+MenuEffectWindowList::MenuEffectWindowList(MenuEffectWindow *window,
+       int x,
+       int y,
+       int w,
+       int h,
+       ArrayList<BC_ListBoxItem*> *plugin_list)
+ : BC_ListBox(x,
+               y,
+               w,
+               h,
+               LISTBOX_TEXT,
+               plugin_list)
+{
+       this->window = window;
+}
+
+int MenuEffectWindowList::handle_event()
+{
+       window->result = get_selection_number(0, 0);
+       window->set_done(0);
+       return 1;
+}
+
+#define PROMPT_TEXT _("Set up effect panel and hit \"OK\"")
+
+MenuEffectPrompt::MenuEffectPrompt(MWindow *mwindow)
+ : BC_Window(_(PROGRAM_NAME ": Effect Prompt"),
+               mwindow->gui->get_abs_cursor_x(1) - 260 / 2,
+               mwindow->gui->get_abs_cursor_y(1) - 300,
+               MenuEffectPrompt::calculate_w(mwindow->gui),
+               MenuEffectPrompt::calculate_h(mwindow->gui),
+               MenuEffectPrompt::calculate_w(mwindow->gui),
+               MenuEffectPrompt::calculate_h(mwindow->gui),
+               0,
+               0,
+               1)
+{
+}
+
+int MenuEffectPrompt::calculate_w(BC_WindowBase *gui)
+{
+       int w = BC_Title::calculate_w(gui, PROMPT_TEXT) + 10;
+       w = MAX(w, BC_OKButton::calculate_w() + BC_CancelButton::calculate_w() + 30);
+       return w;
+}
+
+int MenuEffectPrompt::calculate_h(BC_WindowBase *gui)
+{
+       int h = BC_Title::calculate_h(gui, PROMPT_TEXT);
+       h += BC_OKButton::calculate_h() + 30;
+       return h;
+}
+
+
+void MenuEffectPrompt::create_objects()
+{
+       int x = 10, y = 10;
+       BC_Title *title;
+       add_subwindow(title = new BC_Title(x, y, PROMPT_TEXT));
+       add_subwindow(new BC_OKButton(this));
+       add_subwindow(new BC_CancelButton(this));
+       show_window();
+       raise_window();
+}
+