From 572fa80aa713e723f63e1e1822db614307eea6af Mon Sep 17 00:00:00 2001 From: Sakari Bergen Date: Fri, 26 Sep 2008 08:29:30 +0000 Subject: [PATCH] Add Import from session -functionality git-svn-id: svn://localhost/ardour2/branches/3.0@3805 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/SConscript | 1 + gtk2_ardour/ardour.menus | 1 + gtk2_ardour/editor.h | 1 + gtk2_ardour/editor_actions.cc | 2 + gtk2_ardour/editor_audio_import.cc | 9 + gtk2_ardour/playlist_selector.cc | 29 ++ gtk2_ardour/session_import_dialog.cc | 321 +++++++++++++++ gtk2_ardour/session_import_dialog.h | 101 +++++ libs/ardour/SConscript | 6 + libs/ardour/ardour/audio_playlist_importer.h | 87 ++++ libs/ardour/ardour/audio_region_importer.h | 107 +++++ libs/ardour/ardour/element_import_handler.h | 110 +++++ libs/ardour/ardour/element_importer.h | 122 ++++++ libs/ardour/ardour/location.h | 1 + libs/ardour/ardour/location_importer.h | 63 +++ libs/ardour/ardour/playlist_factory.h | 4 +- libs/ardour/ardour/session.h | 3 +- libs/ardour/ardour/tempo_map_importer.h | 60 +++ libs/ardour/audio_playlist_importer.cc | 221 ++++++++++ libs/ardour/audio_region_importer.cc | 398 +++++++++++++++++++ libs/ardour/element_import_handler.cc | 55 +++ libs/ardour/element_importer.cc | 84 ++++ libs/ardour/location.cc | 8 + libs/ardour/location_importer.cc | 195 +++++++++ libs/ardour/playlist_factory.cc | 10 +- libs/ardour/session.cc | 22 +- libs/ardour/tempo_map_importer.cc | 108 +++++ 27 files changed, 2120 insertions(+), 9 deletions(-) create mode 100644 gtk2_ardour/session_import_dialog.cc create mode 100644 gtk2_ardour/session_import_dialog.h create mode 100644 libs/ardour/ardour/audio_playlist_importer.h create mode 100644 libs/ardour/ardour/audio_region_importer.h create mode 100644 libs/ardour/ardour/element_import_handler.h create mode 100644 libs/ardour/ardour/element_importer.h create mode 100644 libs/ardour/ardour/location_importer.h create mode 100644 libs/ardour/ardour/tempo_map_importer.h create mode 100644 libs/ardour/audio_playlist_importer.cc create mode 100644 libs/ardour/audio_region_importer.cc create mode 100644 libs/ardour/element_import_handler.cc create mode 100644 libs/ardour/element_importer.cc create mode 100644 libs/ardour/location_importer.cc create mode 100644 libs/ardour/tempo_map_importer.cc diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index a3cec81c3d..0388910ebd 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -223,6 +223,7 @@ route_time_axis.cc route_ui.cc selection.cc send_ui.cc +session_import_dialog.cc session_metadata_dialog.cc sfdb_ui.cc simpleline.cc diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index a37645dca8..ed4ea0c2a6 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -18,6 +18,7 @@ + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index a0918241bd..6374b29d06 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1213,6 +1213,7 @@ class Editor : public PublicEditor void add_external_audio_action (Editing::ImportMode); void external_audio_dialog (); + void session_import_dialog (); int check_whether_and_how_to_import(string, bool all_or_nothing = true); bool check_multichannel_status (const std::vector& paths); diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 80b76369d0..fdad190c47 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -754,6 +754,8 @@ Editor::register_actions () ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Import to Region List"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); ActionManager::session_sensitive_actions.push_back (act); + ++ ActionManager::register_action (editor_actions, X_("importFromSession"), _("Import From Session"), mem_fun(*this, &Editor::session_import_dialog)); act = ActionManager::register_toggle_action (editor_actions, X_("toggle-waveform-visible"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility)); ActionManager::track_selection_sensitive_actions.push_back (act); diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index 90202aa3d9..a12eb0857f 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -55,6 +55,7 @@ #include "editing.h" #include "audio_time_axis.h" #include "midi_time_axis.h" +#include "session_import_dialog.h" #include "utils.h" #include "i18n.h" @@ -184,6 +185,14 @@ Editor::external_audio_dialog () } while (keepRunning); } +void +Editor::session_import_dialog () +{ + SessionImportDialog dialog (*session); + ensure_float (dialog); + dialog.run (); +} + typedef std::map > SourceMap; /** diff --git a/gtk2_ardour/playlist_selector.cc b/gtk2_ardour/playlist_selector.cc index 39abfe3dc1..0ca9432fcb 100644 --- a/gtk2_ardour/playlist_selector.cc +++ b/gtk2_ardour/playlist_selector.cc @@ -183,6 +183,35 @@ PlaylistSelector::show_for (RouteUI* ruix) } } + // Add unassigned (imported) playlists to the list + list > unassigned; + session->unassigned_playlists (unassigned); + + TreeModel::Row row; + TreeModel::Row* selected_row = 0; + TreePath this_path; + + row = *(model->append (others.children())); + row[columns.text] = _("Imported"); + proxy = row[columns.playlist]; + proxy.reset (); + + for (list >::iterator p = unassigned.begin(); p != unassigned.end(); ++p) { + TreeModel::Row child_row; + + child_row = *(model->append (row.children())); + child_row[columns.text] = (*p)->name(); + child_row[columns.playlist] = *p; + + if (*p == this_ds->playlist()) { + selected_row = &child_row; + } + + if (selected_row != 0) { + tree.get_selection()->select (*selected_row); + } + } + show_all (); select_connection = tree.get_selection()->signal_changed().connect (mem_fun(*this, &PlaylistSelector::selection_changed)); } diff --git a/gtk2_ardour/session_import_dialog.cc b/gtk2_ardour/session_import_dialog.cc new file mode 100644 index 0000000000..42eb7c56cb --- /dev/null +++ b/gtk2_ardour/session_import_dialog.cc @@ -0,0 +1,321 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "session_import_dialog.h" + +#include + +#include +#include +#include +#include + +#include +#include + +#include "prompter.h" +#include "i18n.h" + +using namespace ARDOUR; + +SessionImportDialog::SessionImportDialog (ARDOUR::Session & target) : + ArdourDialog (_("Import from session")), + target (target), + file_browse_button (_("Browse")) +{ + // File entry + file_entry.set_name ("ImportFileNameEntry"); + file_entry.set_text ("/"); + Gtkmm2ext::set_size_request_to_display_given_text (file_entry, X_("Kg/quite/a/reasonable/size/for/files/i/think"), 5, 8); + + file_browse_button.set_name ("EditorGTKButton"); + file_browse_button.signal_clicked().connect (mem_fun(*this, &SessionImportDialog::browse)); + + file_hbox.set_spacing (5); + file_hbox.set_border_width (5); + file_hbox.pack_start (file_entry, true, true); + file_hbox.pack_start (file_browse_button, false, false); + + file_frame.add (file_hbox); + file_frame.set_border_width (5); + file_frame.set_name ("ImportFrom"); + file_frame.set_label (_("Import from Session")); + + get_vbox()->pack_start (file_frame, false, false); + + // Session browser + session_tree = Gtk::TreeStore::create (sb_cols); + session_browser.set_model (session_tree); + + session_browser.set_name ("SessionBrowser"); + session_browser.append_column (_("Elements"), sb_cols.name); + session_browser.append_column_editable (_("Import"), sb_cols.queued); + session_browser.get_column(0)->set_min_width (180); + session_browser.get_column(1)->set_min_width (40); + session_browser.get_column(1)->set_sizing (Gtk::TREE_VIEW_COLUMN_AUTOSIZE); + + session_scroll.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + session_scroll.add (session_browser); + session_scroll.set_size_request (220, 400); + + // Connect signals + Gtk::CellRendererToggle *toggle = dynamic_cast (session_browser.get_column_cell_renderer (1)); + toggle->signal_toggled().connect(mem_fun (*this, &SessionImportDialog::update)); + session_browser.signal_row_activated().connect(mem_fun (*this, &SessionImportDialog::show_info)); + + get_vbox()->pack_start (session_scroll, false, false); + + // Tooltips + session_browser.set_has_tooltip(); + session_browser.signal_query_tooltip().connect(mem_fun(*this, &SessionImportDialog::query_tooltip)); + + // Buttons + cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + cancel_button->signal_clicked().connect (mem_fun (*this, &SessionImportDialog::end_dialog)); + ok_button = add_button (_("Import"), Gtk::RESPONSE_ACCEPT); + ok_button->signal_clicked().connect (mem_fun (*this, &SessionImportDialog::do_merge)); + + // prompt signals + ElementImporter::Rename.connect (mem_fun (*this, &SessionImportDialog::open_rename_dialog)); + ElementImporter::Prompt.connect (mem_fun (*this, &SessionImportDialog::open_prompt_dialog)); + + // Finalize + show_all(); +} + +void +SessionImportDialog::load_session (const string& filename) +{ + tree.read (filename); + AudioRegionImportHandler *region_handler; + + region_handler = new AudioRegionImportHandler (tree, target); + handlers.push_back (HandlerPtr(region_handler)); + handlers.push_back (HandlerPtr(new AudioPlaylistImportHandler (tree, target, *region_handler))); + handlers.push_back (HandlerPtr(new UnusedAudioPlaylistImportHandler (tree, target, *region_handler))); + handlers.push_back (HandlerPtr(new LocationImportHandler (tree, target))); + handlers.push_back (HandlerPtr(new TempoMapImportHandler (tree, target))); + + fill_list(); + + if (ElementImportHandler::dirty()) { + // Warn user + string txt = _("Some elements had errors in them. Please see the log for details"); + Gtk::MessageDialog msg (txt, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true); + msg.run(); + } +} + +void +SessionImportDialog::fill_list () +{ + session_tree->clear(); + + // Loop through element types + for (HandlerList::iterator handler = handlers.begin(); handler != handlers.end(); ++handler) { + Gtk::TreeModel::iterator iter = session_tree->append(); + Gtk::TreeModel::Row row = *iter; + row[sb_cols.name] = (*handler)->get_info(); + row[sb_cols.queued] = false; + row[sb_cols.element] = ElementPtr(); // "Null" pointer + + // Loop through elements + ElementList &elements = (*handler)->elements; + for (ElementList::iterator element = elements.begin(); element != elements.end(); ++element) { + iter = session_tree->append(row.children()); + Gtk::TreeModel::Row child = *iter; + child[sb_cols.name] = (*element)->get_name(); + child[sb_cols.queued] = false; + child[sb_cols.element] = *element; + } + } +} + +void +SessionImportDialog::browse () +{ + Gtk::FileChooserDialog dialog(_("Import from session"), browse_action()); + dialog.set_transient_for(*this); + dialog.set_filename (file_entry.get_text()); + + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK); + + int result = dialog.run(); + + if (result == Gtk::RESPONSE_OK) { + string filename = dialog.get_filename(); + + if (filename.length()) { + file_entry.set_text (filename); + load_session (filename); + } + } +} + +void +SessionImportDialog::do_merge () +{ + + // element types + Gtk::TreeModel::Children types = session_browser.get_model()->children(); + Gtk::TreeModel::Children::iterator ti; + for (ti = types.begin(); ti != types.end(); ++ti) { + // elements + Gtk::TreeModel::Children elements = ti->children(); + Gtk::TreeModel::Children::iterator ei; + for (ei = elements.begin(); ei != elements.end(); ++ei) { + if ((*ei)[sb_cols.queued]) { + ElementPtr element = (*ei)[sb_cols.element]; + element->move(); + } + } + } + + end_dialog(); + + if (ElementImportHandler::errors()) { + // Warn user + string txt = _("Some elements had errors in them. Please see the log for details"); + Gtk::MessageDialog msg (txt, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true); + msg.run(); + } +} + + +void +SessionImportDialog::update (string path) +{ + Gtk::TreeModel::iterator cell = session_browser.get_model()->get_iter (path); + + // Select all elements if element type is selected + if (path.size() == 1) { + { + // Prompt user for verification + string txt = _("This will select all elements of this type!"); + Gtk::MessageDialog msg (txt, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true); + if (msg.run() == Gtk::RESPONSE_CANCEL) { + (*cell)[sb_cols.queued] = false; + return; + } + } + + Gtk::TreeModel::Children elements = cell->children(); + Gtk::TreeModel::Children::iterator ei; + for (ei = elements.begin(); ei != elements.end(); ++ei) { + ElementPtr element = (*ei)[sb_cols.element]; + if (element->prepare_move()) { + (*ei)[sb_cols.queued] = true; + } else { + (*cell)[sb_cols.queued] = false; // Not all are selected + } + } + return; + } + + ElementPtr element = (*cell)[sb_cols.element]; + if ((*cell)[sb_cols.queued]) { + if (!element->prepare_move()) { + (*cell)[sb_cols.queued] = false; + } + } else { + element->cancel_move(); + } +} + +void +SessionImportDialog::show_info(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) +{ + if (path.size() == 1) { + return; + } + + Gtk::TreeModel::iterator cell = session_browser.get_model()->get_iter (path); + ElementPtr element = (*cell)[sb_cols.element]; + string info = element->get_info(); + + Gtk::MessageDialog msg (info, false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK, true); + msg.run(); +} + +bool +SessionImportDialog::query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr& tooltip) +{ + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* column; + int cell_x, cell_y; + + // Get element + session_browser.get_path_at_pos (x, y, path, column, cell_x, cell_y); + if (path.gobj() == 0) { + return false; + } + Gtk::TreeModel::iterator row = session_browser.get_model()->get_iter (path); + //--row; // FIXME Strange offset in rows, if someone figures this out, please fix + ElementPtr element = (*row)[sb_cols.element]; + if (element.get() == 0) { + return false; + } + + // Prepare tooltip + tooltip->set_text(element->get_info()); + + return true; +} + +void +SessionImportDialog::end_dialog () +{ + hide_all(); + + set_modal (false); + ok_button->set_sensitive(true); +} + +std::pair +SessionImportDialog::open_rename_dialog (string text, string name) +{ + ArdourPrompter prompter(true); + string new_name; + + prompter.set_name ("Prompter"); + prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + prompter.set_prompt (text); + prompter.set_initial_text (name); + + if (prompter.run() == Gtk::RESPONSE_ACCEPT) { + prompter.get_result (new_name); + if (new_name.length()) { + name = new_name; + } + return std::make_pair (true, new_name); + } + return std::make_pair (false, new_name); +} + +bool +SessionImportDialog::open_prompt_dialog (string text) +{ + Gtk::MessageDialog msg (text, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL, true); + if (msg.run() == Gtk::RESPONSE_OK) { + return true; + } + return false; +} diff --git a/gtk2_ardour/session_import_dialog.h b/gtk2_ardour/session_import_dialog.h new file mode 100644 index 0000000000..22cccd1262 --- /dev/null +++ b/gtk2_ardour/session_import_dialog.h @@ -0,0 +1,101 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __session_import_dialog_h__ +#define __session_import_dialog_h__ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "ardour_dialog.h" + +using std::string; + +namespace ARDOUR { + class Session; +} + +class SessionImportDialog : public ArdourDialog +{ + private: + typedef boost::shared_ptr HandlerPtr; + typedef std::list HandlerList; + + typedef boost::shared_ptr ElementPtr; + typedef std::list ElementList; + + public: + SessionImportDialog (ARDOUR::Session & target); + + virtual Gtk::FileChooserAction browse_action() const { return Gtk::FILE_CHOOSER_ACTION_OPEN; } + + private: + + void load_session (const string& filename); + void fill_list (); + void browse (); + void do_merge (); + void end_dialog (); + void update (string path); + void show_info(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column); + bool query_tooltip(int x, int y, bool keyboard_tooltip, const Glib::RefPtr& tooltip); + + std::pair open_rename_dialog (string text, string name); + bool open_prompt_dialog (string text); + + // Data + HandlerList handlers; + XMLTree tree; + ARDOUR::Session ⌖ + + // GUI + Gtk::Frame file_frame; + Gtk::HBox file_hbox; + Gtk::Entry file_entry; + Gtk::Button file_browse_button; + + struct SessionBrowserColumns : public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn queued; + Gtk::TreeModelColumn element; + + SessionBrowserColumns() { add (name); add (queued); add (element); } + }; + + SessionBrowserColumns sb_cols; + Glib::RefPtr session_tree; + Gtk::TreeView session_browser; + Gtk::ScrolledWindow session_scroll; + + Gtk::Button* ok_button; + Gtk::Button* cancel_button; +}; + +#endif diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 352d1b26f6..156daf2bc0 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -36,6 +36,7 @@ audio_buffer.cc audio_diskstream.cc audio_library.cc audio_playlist.cc +audio_playlist_importer.cc audio_port.cc audio_track.cc audioanalyser.cc @@ -43,6 +44,7 @@ audioengine.cc audiofile_tagger.cc audiofilesource.cc audioregion.cc +audio_region_importer.cc audiosource.cc auditioner.cc auto_bundle.cc @@ -64,6 +66,8 @@ cycle_timer.cc default_click.cc directory_names.cc diskstream.cc +element_importer.cc +element_import_handler.cc enums.cc event_type_map.cc export_channel_configuration.cc @@ -96,6 +100,7 @@ jack_port.cc jack_slave.cc ladspa_plugin.cc location.cc +location_importer.cc meter.cc midi_buffer.cc midi_clock_slave.cc @@ -159,6 +164,7 @@ svn_revision.cc tape_file_matcher.cc template_utils.cc tempo.cc +tempo_map_importer.cc track.cc transient_detector.cc user_bundle.cc diff --git a/libs/ardour/ardour/audio_playlist_importer.h b/libs/ardour/ardour/audio_playlist_importer.h new file mode 100644 index 0000000000..a3e72f18c8 --- /dev/null +++ b/libs/ardour/ardour/audio_playlist_importer.h @@ -0,0 +1,87 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_audio_playlist_importer_h__ +#define __ardour_audio_playlist_importer_h__ + +#include + +#include + +#include +#include + +#include +#include +#include + +#include "i18n.h" + +namespace ARDOUR { + +class AudioRegionImportHandler; +class AudioRegionImporter; + +class AudioPlaylistImportHandler : public ElementImportHandler +{ + public: + AudioPlaylistImportHandler (XMLTree const & source, Session & session, AudioRegionImportHandler & region_handler, const char * nodename = "Playlists"); + virtual ~AudioPlaylistImportHandler () {} + virtual string get_info () const; + + void get_regions (XMLNode const & node, ElementList & list); + void update_region_id (XMLProperty* id_prop); + + protected: + AudioRegionImportHandler & region_handler; +}; + +class UnusedAudioPlaylistImportHandler : public AudioPlaylistImportHandler +{ + public: + UnusedAudioPlaylistImportHandler (XMLTree const & source, Session & session, AudioRegionImportHandler & region_handler) : + AudioPlaylistImportHandler (source, session, region_handler, X_("UnusedPlaylists")) { } + string get_info () const { return _("Audio Playlists (unused)"); } +}; + +class AudioPlaylistImporter : public ElementImporter +{ + public: + AudioPlaylistImporter (XMLTree const & source, Session & session, AudioPlaylistImportHandler & handler, XMLNode const & node); + + string get_info () const; + bool prepare_move (); + void cancel_move (); + void move (); + + void set_diskstream (PBD::ID const & id); + + private: + typedef std::list > RegionList; + + AudioPlaylistImportHandler & handler; + XMLNode xml_playlist; + PBD::ID diskstream_id; + RegionList regions; +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/ardour/audio_region_importer.h b/libs/ardour/ardour/audio_region_importer.h new file mode 100644 index 0000000000..a2205390ab --- /dev/null +++ b/libs/ardour/ardour/audio_region_importer.h @@ -0,0 +1,107 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_audio_region_importer_h__ +#define __ardour_audio_region_importer_h__ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace ARDOUR { + +class Region; + +class AudioRegionImportHandler : public ElementImportHandler +{ + public: + // Inerface implementation + AudioRegionImportHandler (XMLTree const & source, Session & session); + string get_info () const; + + void create_regions_from_children (XMLNode const & node, ElementList & list); + + // Source management + bool check_source (string const & filename) const; + void add_source (string const & filename, boost::shared_ptr const & source); + boost::shared_ptr const & get_source (string const & filename) const; + + // Id management + void register_id (PBD::ID & old_id, PBD::ID & new_id); + PBD::ID const & get_new_id (PBD::ID & old_id) const; + + private: + // Source management + typedef std::map > SourceMap; + typedef std::pair > SourcePair; + SourceMap sources; + + // Id management + typedef std::map IdMap; + typedef std::pair IdPair; + IdMap id_map; +}; + +class AudioRegionImporter : public ElementImporter +{ + public: + AudioRegionImporter (XMLTree const & source, Session & session, AudioRegionImportHandler & handler, XMLNode const & node); + + // Interface implementation + string get_info () const; + bool prepare_move (); + void cancel_move (); + void move (); + + // other stuff + void add_sources_to_session (); + XMLNode const & get_xml (); + + private: + + XMLNode xml_region; + AudioRegionImportHandler & handler; + PBD::ID old_id; + PBD::ID id; + std::list filenames; + + bool parse_xml_region (); + bool parse_source_xml (); + PBD::sys::path get_sound_dir (XMLTree const & tree); + + void prepare_region (); + void prepare_sources (); + std::vector > region; + bool region_prepared; + bool sources_prepared; +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/ardour/element_import_handler.h b/libs/ardour/ardour/element_import_handler.h new file mode 100644 index 0000000000..9393c31559 --- /dev/null +++ b/libs/ardour/ardour/element_import_handler.h @@ -0,0 +1,110 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_element_import_handler_h__ +#define __ardour_element_import_handler_h__ + +#include +#include + +#include + +using std::string; +class XMLTree; + +namespace ARDOUR { + +class Session; +class ElementImporter; + +/// Virtual interface class for element import handlers +class ElementImportHandler +{ + public: + typedef boost::shared_ptr ElementPtr; + typedef std::list ElementList; + + /** ElementImportHandler constructor + * The constructor should find everything from the XML Tree it can handle + * and create respective Elements stored in elements. + * + * @param source XML tree to be parsed + * @see elements + */ + ElementImportHandler (XMLTree const & source, ARDOUR::Session & session) : + source (source), session (session) { } + + virtual ~ElementImportHandler (); + + /** Gets a textual representation of the element type + * @return textual representation of element type + */ + virtual string get_info () const = 0; + + /// Elements this handler handles + ElementList elements; + + /* For checking duplicates names against queued elements */ + + /** Checks whether or not an element with some name is queued or not + * @param name name to check + * @return true if name is not used + */ + bool check_name (const string & name) const; + + /// Adds name to the list of used names + void add_name (string name); + + /// Removes name from the list of used names + void remove_name (const string & name); + + /// Checks wheter or not all elements can be imported cleanly + static bool dirty () { return _dirty; } + + /// Sets handler dirty + static void set_dirty () { _dirty = true; } + + /// Checks wheter or not all elements were imported cleanly + static bool errors () { return _errors; } + + /// Sets handler dirty + static void set_errors () { _errors = true; } + + protected: + /// Source session XML tree + XMLTree const & source; + + /// Destination session + ARDOUR::Session & session; + + /// Session XML readability + static bool _dirty; + + /// Errors post initialization + static bool _errors; + + private: + /// List of names for duplicate checking + std::list names; +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/ardour/element_importer.h b/libs/ardour/ardour/element_importer.h new file mode 100644 index 0000000000..34ab0a7cc6 --- /dev/null +++ b/libs/ardour/ardour/element_importer.h @@ -0,0 +1,122 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_element_importer_h__ +#define __ardour_element_importer_h__ + +#include +#include + +#include + +#include + +using std::string; + +class XMLTree; +namespace ARDOUR { + +class Session; + +/// Virtual interface class for element importers +class ElementImporter +{ + public: + + ElementImporter (XMLTree const & source, ARDOUR::Session & session); + virtual ~ElementImporter () {}; + + /** Returns the element name + * @return the name of the element + */ + virtual string get_name () const { return name; }; + + /** Gets a textual representation of the element + * @return a textual representation on this specific element + */ + virtual string get_info () const = 0; + + /** Prepares to move element + * Should take care of all tasks that need to be done + * before moving the element. This includes prompting + * the user for more information if necessary. + * + * If the element can be moved, queued should be set to true. + * + * @return whther or not the element could be prepared for moving + */ + virtual bool prepare_move () = 0; + + /** Cancels moving of element + * If the element has been set to be moved, this cancels the move. + * queued should be set to false. + */ + virtual void cancel_move () = 0; + + /** Moves the element to the taget session + * In addition to actually adding the element to the session + * changing ids, renaming files etc. should be taken care of. + */ + virtual void move () = 0; + + /// Check if element is broken. Cannot be moved if broken. + bool broken () { return _broken; } + + /// Signal that requests for anew name + static sigc::signal , string, string> Rename; + + /// Signal for ok/cancel prompting + static sigc::signal Prompt; + + protected: + /// Source XML-tree + XMLTree const & source; + + /// Target session + ARDOUR::Session & session; + + /// Ture if the element has been prepared and queued for importing + bool queued; + + /// Name of element + string name; + + /// The sample rate of the session from which we are importing + nframes_t sample_rate; + + /// Converts smpte time to a string + string smpte_to_string(SMPTE::Time & time) const; + + /// Converts samples so that times match the sessions sample rate + nframes_t rate_convert_samples (nframes_t samples) const; + + /// Converts samples so that times match the sessions sample rate (for straight use in XML) + string rate_convert_samples (string const & samples) const; + + /// Set element broken + void set_broken () { _broken = true; } + + private: + bool _broken; +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h index d5b672a89d..ae0ec5acd8 100644 --- a/libs/ardour/ardour/location.h +++ b/libs/ardour/ardour/location.h @@ -99,6 +99,7 @@ class Location : public PBD::StatefulDestructible void set_cd (bool yn, void *src); void set_is_end (bool yn, void* src); void set_is_start (bool yn, void* src); + void set_is_range_marker (bool yn, void* src); bool is_auto_punch () const { return _flags & IsAutoPunch; } bool is_auto_loop () const { return _flags & IsAutoLoop; } diff --git a/libs/ardour/ardour/location_importer.h b/libs/ardour/ardour/location_importer.h new file mode 100644 index 0000000000..7066151383 --- /dev/null +++ b/libs/ardour/ardour/location_importer.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_location_importer_h__ +#define __ardour_location_importer_h__ + +#include +#include + +#include + +#include +#include +#include + +namespace ARDOUR { + +class LocationImportHandler : public ElementImportHandler +{ + public: + LocationImportHandler (XMLTree const & source, Session & session); + string get_info () const; +}; + +class LocationImporter : public ElementImporter +{ + public: + LocationImporter (XMLTree const & source, Session & session, LocationImportHandler & handler, XMLNode const & node); + ~LocationImporter (); + + string get_info () const; + bool prepare_move (); + void cancel_move (); + void move (); + + private: + LocationImportHandler & handler; + XMLNode xml_location; + Location * location; + + void parse_xml (); +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/ardour/playlist_factory.h b/libs/ardour/ardour/playlist_factory.h index a28e2611d9..239fa49a04 100644 --- a/libs/ardour/ardour/playlist_factory.h +++ b/libs/ardour/ardour/playlist_factory.h @@ -31,9 +31,9 @@ class Session; class PlaylistFactory { public: - static sigc::signal > PlaylistCreated; + static sigc::signal, bool> PlaylistCreated; - static boost::shared_ptr create (Session&, const XMLNode&, bool hidden = false); + static boost::shared_ptr create (Session&, const XMLNode&, bool hidden = false, bool unused = false); static boost::shared_ptr create (DataType type, Session&, string name, bool hidden = false); static boost::shared_ptr create (boost::shared_ptr, string name, bool hidden = false); static boost::shared_ptr create (boost::shared_ptr, nframes_t start, nframes_t cnt, string name, bool hidden = false); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index fa074071c5..83e3200224 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -685,7 +685,8 @@ class Session : public PBD::StatefulDestructible /* playlist management */ boost::shared_ptr playlist_by_name (string name); - void add_playlist (boost::shared_ptr); + void unassigned_playlists (std::list > & list); + void add_playlist (boost::shared_ptr, bool unused = false); sigc::signal > PlaylistAdded; sigc::signal > PlaylistRemoved; diff --git a/libs/ardour/ardour/tempo_map_importer.h b/libs/ardour/ardour/tempo_map_importer.h new file mode 100644 index 0000000000..6c2a057943 --- /dev/null +++ b/libs/ardour/ardour/tempo_map_importer.h @@ -0,0 +1,60 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __ardour_tempo_map_importer_h__ +#define __ardour_tempo_map_importer_h__ + +#include + +#include + +#include +#include +#include +#include + +namespace ARDOUR { + +class TempoMapImportHandler : public ElementImportHandler +{ + public: + TempoMapImportHandler (XMLTree const & source, Session & session); + string get_info () const; +}; + +class TempoMapImporter : public ElementImporter +{ + private: + typedef boost::shared_ptr XMLNodePtr; + public: + TempoMapImporter (XMLTree const & source, Session & session, XMLNode const & node); + + virtual string get_info () const; + virtual bool prepare_move (); + virtual void cancel_move (); + virtual void move (); + + private: + XMLNode xml_tempo_map; +}; + +} // namespace ARDOUR + +#endif diff --git a/libs/ardour/audio_playlist_importer.cc b/libs/ardour/audio_playlist_importer.cc new file mode 100644 index 0000000000..d6a2b2f9fb --- /dev/null +++ b/libs/ardour/audio_playlist_importer.cc @@ -0,0 +1,221 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace PBD; +using namespace ARDOUR; + +/**** Handler ***/ +AudioPlaylistImportHandler::AudioPlaylistImportHandler (XMLTree const & source, Session & session, AudioRegionImportHandler & region_handler, const char * nodename) : + ElementImportHandler (source, session), + region_handler (region_handler) +{ + XMLNode const * root = source.root(); + XMLNode const * playlists; + + if (!(playlists = root->child (nodename))) { + throw failed_constructor(); + } + + XMLNodeList const & pl_children = playlists->children(); + for (XMLNodeList::const_iterator it = pl_children.begin(); it != pl_children.end(); ++it) { + const XMLProperty* type = (*it)->property("type"); + if ( !type || type->value() == "audio" ) { + try { + elements.push_back (ElementPtr ( new AudioPlaylistImporter (source, session, *this, **it))); + } catch (failed_constructor err) { + set_dirty(); + } + } + } +} + +string +AudioPlaylistImportHandler::get_info () const +{ + return _("Audio Playlists"); +} + +void +AudioPlaylistImportHandler::get_regions (XMLNode const & node, ElementList & list) +{ + region_handler.create_regions_from_children (node, list); +} + +void +AudioPlaylistImportHandler::update_region_id (XMLProperty* id_prop) +{ + PBD::ID old_id (id_prop->value()); + PBD::ID new_id (region_handler.get_new_id (old_id)); + id_prop->set_value (new_id.to_s()); +} + +/*** AudioPlaylistImporter ***/ +AudioPlaylistImporter::AudioPlaylistImporter (XMLTree const & source, Session & session, AudioPlaylistImportHandler & handler, XMLNode const & node) : + ElementImporter (source, session), + handler (handler), + xml_playlist (node), + diskstream_id ("0") +{ + bool ds_ok = false; + + // Populate region list + ElementImportHandler::ElementList elements; + handler.get_regions (node, elements); + for (ElementImportHandler::ElementList::iterator it = elements.begin(); it != elements.end(); ++it) { + regions.push_back (boost::dynamic_pointer_cast (*it)); + } + + // Parse XML + XMLPropertyList const & props = xml_playlist.properties(); + for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) { + string prop = (*it)->name(); + if (!prop.compare("type") || !prop.compare("frozen")) { + // All ok + } else if (!prop.compare("name")) { + name = (*it)->value(); + } else if (!prop.compare("orig_diskstream_id")) { + ds_ok = true; + } else { + std::cerr << string_compose (X_("AudioPlaylistImporter did not recognise XML-property \"%1\""), prop) << endmsg; + } + } + + if (!ds_ok) { + error << string_compose (X_("AudioPlaylistImporter (%1): did not find XML-property \"orig_diskstream_id\" which is mandatory"), name) << endmsg; + throw failed_constructor(); + } +} + +string +AudioPlaylistImporter::get_info () const +{ + XMLNodeList children = xml_playlist.children(); + unsigned int regions = 0; + std::ostringstream oss; + + for (XMLNodeIterator it = children.begin(); it != children.end(); it++) { + if ((*it)->name() == "Region") { + ++regions; + } + } + + oss << regions << " "; + + if (regions == 1) { + oss << _("region"); + } else { + oss << _("regions"); + } + + return oss.str(); +} + +bool +AudioPlaylistImporter::prepare_move () +{ + // Rename + while (session.playlist_by_name (name) || !handler.check_name (name)) { + std::pair rename_pair = Rename (_("A playlist with this name already exists, please rename it."), name); + if (!rename_pair.first) { + return false; + } + name = rename_pair.second; + } + xml_playlist.property ("name")->set_value (name); + handler.add_name (name); + + queued = true; + return true; +} + +void +AudioPlaylistImporter::cancel_move () +{ + handler.remove_name (name); + queued = false; +} + +void +AudioPlaylistImporter::move () +{ + boost::shared_ptr playlist; + + // Update diskstream id + xml_playlist.property ("orig_diskstream_id")->set_value (diskstream_id.to_s()); + + // Update region XML in playlist and prepare sources + xml_playlist.remove_nodes("Region"); + for (RegionList::iterator it = regions.begin(); it != regions.end(); ++it) { + xml_playlist.add_child_copy ((*it)->get_xml()); + (*it)->add_sources_to_session(); + if ((*it)->broken()) { + handler.set_dirty(); + set_broken(); + return; // TODO clean up? + } + } + + // Update region ids in crossfades + XMLNodeList crossfades = xml_playlist.children("Crossfade"); + for (XMLNodeIterator it = crossfades.begin(); it != crossfades.end(); ++it) { + XMLProperty* in = (*it)->property("in"); + XMLProperty* out = (*it)->property("out"); + if (!in || !out) { + error << string_compose (X_("AudioPlaylistImporter (%1): did not find the \"in\" or \"out\" property from a crossfade"), name) << endmsg; + } + + handler.update_region_id (in); + handler.update_region_id (out); + + // rate convert length and position + XMLProperty* length = (*it)->property("length"); + if (length) { + length->set_value (rate_convert_samples (length->value())); + } + + XMLProperty* position = (*it)->property("position"); + if (position) { + position->set_value (rate_convert_samples (position->value())); + } + } + + // Create playlist + playlist = PlaylistFactory::create (session, xml_playlist, false, true); +} + +void +AudioPlaylistImporter::set_diskstream (PBD::ID const & id) +{ + diskstream_id = id; +} + diff --git a/libs/ardour/audio_region_importer.cc b/libs/ardour/audio_region_importer.cc new file mode 100644 index 0000000000..ab8261a369 --- /dev/null +++ b/libs/ardour/audio_region_importer.cc @@ -0,0 +1,398 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +/**** Handler ***/ +AudioRegionImportHandler::AudioRegionImportHandler (XMLTree const & source, Session & session) : + ElementImportHandler (source, session) +{ + XMLNode const * root = source.root(); + XMLNode const * regions; + + if (!(regions = root->child (X_("Regions")))) { + throw failed_constructor(); + } + + create_regions_from_children (*regions, elements); +} + +void +AudioRegionImportHandler::create_regions_from_children (XMLNode const & node, ElementList & list) +{ + XMLNodeList const & children = node.children(); + for (XMLNodeList::const_iterator it = children.begin(); it != children.end(); ++it) { + XMLProperty const * type = (*it)->property("type"); + if (!(*it)->name().compare ("Region") && (!type || type->value() == "audio") ) { + try { + list.push_back (ElementPtr ( new AudioRegionImporter (source, session, *this, **it))); + } catch (failed_constructor err) { + set_dirty(); + } + } + } +} + +string +AudioRegionImportHandler::get_info () const +{ + return _("Audio Regions"); +} + +bool +AudioRegionImportHandler::check_source (string const & filename) const +{ + return (sources.find (filename) != sources.end()); +} + +void +AudioRegionImportHandler::add_source (string const & filename, boost::shared_ptr const & source) +{ + sources.insert (SourcePair (filename, source)); +} + +boost::shared_ptr const & +AudioRegionImportHandler::get_source (string const & filename) const +{ + return (sources.find (filename))->second; +} + +void +AudioRegionImportHandler::register_id (PBD::ID & old_id, PBD::ID & new_id) +{ + id_map.insert (IdPair (old_id, new_id)); +} + +PBD::ID const & +AudioRegionImportHandler::get_new_id (PBD::ID & old_id) const +{ + return (id_map.find (old_id))->second; +} + +/*** AudioRegionImporter ***/ +AudioRegionImporter::AudioRegionImporter (XMLTree const & source, Session & session, AudioRegionImportHandler & handler, XMLNode const & node) : + ElementImporter (source, session), + xml_region (node), + handler (handler), + old_id ("0"), + region_prepared (false), + sources_prepared (false) +{ + if (!parse_xml_region () || !parse_source_xml ()) { + throw failed_constructor(); + } + handler.register_id (old_id, id); +} + +string +AudioRegionImporter::get_info () const +{ + nframes_t length, position; + SMPTE::Time length_time, position_time; + std::ostringstream oss; + + // Get sample positions + std::istringstream iss_length(xml_region.property ("length")->value()); + iss_length >> length; + std::istringstream iss_position(xml_region.property ("position")->value()); + iss_position >> position; + + // Convert to smpte + session.sample_to_smpte(length, length_time, true, false); + session.sample_to_smpte(position, position_time, true, false); + + // return info + oss << _("Length: ") << + smpte_to_string(length_time) << + _("\nPosition: ") << + smpte_to_string(position_time) << + _("\nChannels: ") << + xml_region.property ("channels")->value(); + + + return oss.str(); +} + +bool +AudioRegionImporter::prepare_move () +{ + queued = true; + return true; +} + +void +AudioRegionImporter::cancel_move () +{ + queued = false; +} + +void +AudioRegionImporter::move () +{ + if (!region_prepared) { + prepare_region(); + if (!region_prepared) { + return; + } + } + + if (broken()) { + return; + } + + session.add_regions (region); +} + +bool +AudioRegionImporter::parse_xml_region () +{ + XMLPropertyList const & props = xml_region.properties();; + bool id_ok = false; + bool name_ok = false; + + for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) { + string prop = (*it)->name(); + if (!prop.compare ("type") || !prop.compare ("stretch") || + !prop.compare ("shift") || !prop.compare ("first_edit") || + !prop.compare ("layer") || !prop.compare ("flags") || + !prop.compare ("scale-gain") || !prop.compare("channels") || + prop.find ("master-source-") == 0 || prop.find ("source-") == 0) { + // All ok + } else if (!prop.compare ("start") || !prop.compare ("length") || + !prop.compare ("position") || !prop.compare ("ancestral-start") || + !prop.compare ("ancestral-length") || !prop.compare ("sync-position")) { + // Sample rate conversion + (*it)->set_value (rate_convert_samples ((*it)->value())); + } else if (!prop.compare("id")) { + // get old id and update id + old_id = (*it)->value(); + (*it)->set_value (id.to_s()); + id_ok = true; + } else if (!prop.compare("name")) { + // rename region if necessary + name = (*it)->value(); + name = session.new_region_name (name); + (*it)->set_value (name); + name_ok = true; + } else { + std::cerr << string_compose (X_("AudioRegionImporter (%1): did not recognise XML-property \"%1\""), name, prop) << endmsg; + } + } + + if (!id_ok) { + error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"id\""), name) << endmsg; + return false; + } + + if (!name_ok) { + error << X_("AudioRegionImporter: did not find necessary XML-property \"name\"") << endmsg; + return false; + } + + return true; +} + +bool +AudioRegionImporter::parse_source_xml () +{ + uint32_t channels; + char buf[128]; + PBD::sys::path source_dir = get_sound_dir (source); + PBD::sys::path source_path; + XMLNode * source_node; + XMLProperty *prop; + + // Get XML for sources + if (!(source_node = source.root()->child (X_("Sources")))) { + return false; + } + XMLNodeList const & sources = source_node->children(); + + // Get source for each channel + if (!(prop = xml_region.property ("channels"))) { + error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"channels\""), name) << endmsg; + return false; + } + + channels = atoi (prop->value()); + for (uint32_t i = 0; i < channels; ++i) { + bool source_found = false; + + // Get id for source-n + snprintf (buf, sizeof(buf), X_("source-%d"), i); + prop = xml_region.property (buf); + if (!prop) { + error << string_compose (X_("AudioRegionImporter (%1): did not find necessary XML-property \"%3\""), name, buf) << endmsg; + return false; + } + string source_id = prop->value(); + + // Get source + for (XMLNodeList::const_iterator it = sources.begin(); it != sources.end(); it++) { + prop = (*it)->property ("id"); + if (prop && !source_id.compare (prop->value())) { + source_path = source_dir; + prop = (*it)->property ("name"); + if (!prop) { + error << string_compose (X_("AudioRegionImporter (%1): source %2 has no \"name\" property"), name, source_id) << endmsg; + return false; + } + source_path /= prop->value(); + filenames.push_back (source_path.to_string()); + + source_found = true; + break; + } + } + + if (!source_found) { + error << string_compose (X_("AudioRegionImporter (%1): could not find all necessary sources"), name) << endmsg; + return false; + } + } + + return true; +} + +PBD::sys::path +AudioRegionImporter::get_sound_dir (XMLTree const & tree) +{ + PBD::sys::path source_dir = tree.filename(); + source_dir = source_dir.branch_path(); + SessionDirectory session_dir(source_dir); + source_dir = session_dir.sound_path(); + + return source_dir; +} + +void +AudioRegionImporter::prepare_region () +{ + if (region_prepared) { + return; + } + + SourceList source_list; + prepare_sources(); + + // Create source list + for (std::list::iterator it = filenames.begin(); it != filenames.end(); ++it) { + source_list.push_back (handler.get_source (*it)); + } + + // create region and update XML + region.push_back (RegionFactory::create (source_list, xml_region)); + if (*region.begin()) { + xml_region = (*region.begin())->get_state(); + } else { + error << string_compose (X_("AudioRegionImporter (%1): could not construct Region"), name) << endmsg; + handler.set_errors(); + } + + region_prepared = true; +} + +void +AudioRegionImporter::prepare_sources () +{ + if (sources_prepared) { + return; + } + + Session::import_status status; + + // Get sources that still need to be imported + for (std::list::iterator it = filenames.begin(); it != filenames.end(); ++it) { + if (!handler.check_source (*it)) { + status.paths.push_back (*it); + } + } + + // Prepare rest of import struct TODO quality + status.replace_existing_source = false; + status.done = false; + status.cancel = false; + status.freeze = false; + status.progress = 0.0; + status.quality = SrcBest; + + // import files + // TODO: threading & exception handling + session.import_audiofiles (status); + + // Add imported sources to handlers map + std::vector::iterator file_it = status.paths.begin(); + for (SourceList::iterator source_it = status.sources.begin(); source_it != status.sources.end(); ++source_it) { + if (*source_it) { + handler.add_source(*file_it, *source_it); + } else { + error << string_compose (X_("AudioRegionImporter (%1): could not import all necessary sources"), name) << endmsg; + handler.set_errors(); + set_broken(); + } + + ++file_it; + } + + sources_prepared = true; +} + +void +AudioRegionImporter::add_sources_to_session () +{ + if (!sources_prepared) { + prepare_sources(); + } + + if (broken()) { + return; + } + + for (std::list::iterator it = filenames.begin(); it != filenames.end(); ++it) { + session.add_source (handler.get_source (*it)); + } +} + +XMLNode const & +AudioRegionImporter::get_xml () +{ + if(!region_prepared) { + prepare_region(); + } + + return xml_region; +} diff --git a/libs/ardour/element_import_handler.cc b/libs/ardour/element_import_handler.cc new file mode 100644 index 0000000000..013dd3fe44 --- /dev/null +++ b/libs/ardour/element_import_handler.cc @@ -0,0 +1,55 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +using namespace ARDOUR; + +bool ElementImportHandler::_dirty = false; +bool ElementImportHandler::_errors = false; + +ElementImportHandler::~ElementImportHandler () +{ + _dirty = false; + _errors = false; +} + +bool +ElementImportHandler::check_name (const string & name) const +{ + return std::find (names.begin(), names.end(), name) == names.end(); +} + +void +ElementImportHandler::add_name (string name) +{ + names.push_back (name); +} + +void +ElementImportHandler::remove_name (const string & name) +{ + std::list::iterator it = std::find (names.begin(), names.end(), name); + if (it != names.end()) { + names.erase(it); + } +} diff --git a/libs/ardour/element_importer.cc b/libs/ardour/element_importer.cc new file mode 100644 index 0000000000..0312f0ef15 --- /dev/null +++ b/libs/ardour/element_importer.cc @@ -0,0 +1,84 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include +#include + +#include +#include + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +sigc::signal , string, string> ElementImporter::Rename; +sigc::signal ElementImporter::Prompt; + +ElementImporter::ElementImporter (XMLTree const & source, ARDOUR::Session & session) : + source (source), + session(session), + queued (false), + _broken (false) +{ + // Get samplerate + XMLProperty *prop; + prop = source.root()->property ("sample-rate"); + if (prop) { + std::istringstream iss (prop->value()); + iss >> sample_rate; + } +} + +string +ElementImporter::smpte_to_string(SMPTE::Time & time) const +{ + std::ostringstream oss; + oss << std::setfill('0') << std::right << + std::setw(2) << + time.hours << ":" << + std::setw(2) << + time.minutes << ":" << + std::setw(2) << + time.seconds << ":" << + std::setw(2) << + time.frames; + + return oss.str(); +} + +nframes_t +ElementImporter::rate_convert_samples (nframes_t samples) const +{ + if (sample_rate == session.frame_rate()) { + return samples; + } + + // +0.5 for proper rounding + return static_cast (samples * (static_cast (session.nominal_frame_rate()) / sample_rate) + 0.5); +} + +string +ElementImporter::rate_convert_samples (string const & samples) const +{ + return to_string (rate_convert_samples (atoi (samples)), std::dec); +} diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc index 8dd4bbc636..be34e34349 100644 --- a/libs/ardour/location.cc +++ b/libs/ardour/location.cc @@ -246,6 +246,14 @@ Location::set_is_start (bool yn, void *src) } } +void +Location::set_is_range_marker (bool yn, void *src) +{ + if (set_flag_internal (yn, IsRangeMarker)) { + FlagsChanged (this, src); /* EMIT SIGNAL */ + } +} + void Location::set_auto_punch (bool yn, void *src) { diff --git a/libs/ardour/location_importer.cc b/libs/ardour/location_importer.cc new file mode 100644 index 0000000000..31f19ef164 --- /dev/null +++ b/libs/ardour/location_importer.cc @@ -0,0 +1,195 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include +#include + +#include +#include +#include + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +/**** Handler ***/ +LocationImportHandler::LocationImportHandler (XMLTree const & source, Session & session) : + ElementImportHandler (source, session) +{ + XMLNode const *root = source.root(); + XMLNode const * location_node; + + if (!(location_node = root->child ("Locations"))) { + throw failed_constructor(); + } + + // Construct importable locations + XMLNodeList const & locations = location_node->children(); + for (XMLNodeList::const_iterator it = locations.begin(); it != locations.end(); it++) { + try { + elements.push_back (ElementPtr ( new LocationImporter (source, session, *this, **it))); + } catch (failed_constructor err) { + _dirty = true; + } + } +} + +string +LocationImportHandler::get_info () const +{ + return _("Locations"); +} + +/*** LocationImporter ***/ +LocationImporter::LocationImporter (XMLTree const & source, Session & session, LocationImportHandler & handler, XMLNode const & node) : + ElementImporter (source, session), + handler (handler), + xml_location (node), + location (0) + { + // Parse XML + bool name_ok = false; + XMLPropertyList props = xml_location.properties(); + + for (XMLPropertyIterator it = props.begin(); it != props.end(); ++it) { + string prop = (*it)->name(); + if (!prop.compare ("id") || !prop.compare ("flags") || !prop.compare ("locked")) { + // All ok + } else if (!prop.compare ("start") || !prop.compare ("end")) { + // Sample rate conversion + (*it)->set_value (rate_convert_samples ((*it)->value())); + } else if (!prop.compare ("name")) { + // rename region if necessary + name = (*it)->value(); + name_ok = true; + } else { + std::cerr << string_compose (X_("LocationImporter did not recognise XML-property \"%1\""), prop) << endmsg; + } + } + + if (!name_ok) { + error << X_("LocationImporter did not find necessary XML-property \"name\"") << endmsg; + throw failed_constructor(); + } +} + +LocationImporter::~LocationImporter () +{ + if (!queued && location) { + delete location; + } +} + +string +LocationImporter::get_info () const +{ + nframes_t start, end; + SMPTE::Time start_time, end_time; + + // Get sample positions + std::istringstream iss_start (xml_location.property ("start")->value()); + iss_start >> start; + std::istringstream iss_end (xml_location.property ("end")->value()); + iss_end >> end; + + // Convert to smpte + session.sample_to_smpte (start, start_time, true, false); + session.sample_to_smpte (end, end_time, true, false); + + // return info + std::ostringstream oss; + if (start == end) { + oss << _("Location: ") << smpte_to_string (start_time); + } else { + oss << _("Range\nstart: ") << smpte_to_string (start_time) << + _("\nend: ") << smpte_to_string (end_time); + } + + return oss.str(); +} + +bool +LocationImporter::prepare_move () +{ + try { + Location const original (xml_location); + location = new Location (original); // Updates id + } catch (failed_constructor& err) { + throw std::runtime_error (X_("Error in session file!")); + return false; + } + + std::pair rename_pair; + + if (location->is_auto_punch()) { + rename_pair = Rename (_("The location is the Punch range. It will be imported as a normal range.\nYou may rename the imported location:"), name); + if (!rename_pair.first) { + return false; + } + + name = rename_pair.second; + location->set_auto_punch (false, this); + location->set_is_range_marker (true, this); + } + + if (location->is_auto_loop()) { + rename_pair = Rename (_("The location is a Loop range. It will be imported as a normal range.\nYou may rename the imported location:"), name); + if (!rename_pair.first) { return false; } + + location->set_auto_loop (false, this); + location->set_is_range_marker (true, this); + } + + // duplicate name checking + Locations::LocationList const & locations(session.locations()->list()); + for (Locations::LocationList::const_iterator it = locations.begin(); it != locations.end(); ++it) { + if (!((*it)->name().compare (location->name())) || !handler.check_name (location->name())) { + rename_pair = Rename (_("A location with that name already exists.\nYou may rename the imported location:"), name); + if (!rename_pair.first) { return false; } + name = rename_pair.second; + } + } + + location->set_name (name); + queued = true; + return true; +} + +void +LocationImporter::cancel_move () +{ + queued = false; + if (location) { + delete location; + location = 0; + } +} + +void +LocationImporter::move () +{ + if (!queued) { + return; + } + session.locations()->add (location); +} diff --git a/libs/ardour/playlist_factory.cc b/libs/ardour/playlist_factory.cc index a801bae76c..129ff514af 100644 --- a/libs/ardour/playlist_factory.cc +++ b/libs/ardour/playlist_factory.cc @@ -29,10 +29,10 @@ using namespace ARDOUR; using namespace PBD; -sigc::signal > PlaylistFactory::PlaylistCreated; +sigc::signal, bool> PlaylistFactory::PlaylistCreated; boost::shared_ptr -PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden) +PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden, bool unused) { const XMLProperty* type = node.property("type"); @@ -46,7 +46,7 @@ PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden) pl->set_region_ownership (); if (pl && !hidden) { - PlaylistCreated (pl); + PlaylistCreated (pl, unused); } return pl; } @@ -62,7 +62,7 @@ PlaylistFactory::create (DataType type, Session& s, string name, bool hidden) pl = boost::shared_ptr (new MidiPlaylist (s, name, hidden)); if (pl && !hidden) { - PlaylistCreated (pl); + PlaylistCreated (pl, false); } return pl; @@ -84,7 +84,7 @@ PlaylistFactory::create (boost::shared_ptr old, string name, boo } if (pl && !hidden) { - PlaylistCreated (pl); + PlaylistCreated (pl, false); } return pl; diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ffa7d17761..30f93f9646 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -3368,7 +3368,23 @@ Session::playlist_by_name (string name) } void -Session::add_playlist (boost::shared_ptr playlist) +Session::unassigned_playlists (std::list > & list) +{ + Glib::Mutex::Lock lm (playlist_lock); + for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) { + if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { + list.push_back (*i); + } + } + for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) { + if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) { + list.push_back (*i); + } + } +} + +void +Session::add_playlist (boost::shared_ptr playlist, bool unused) { if (playlist->hidden()) { return; @@ -3383,6 +3399,10 @@ Session::add_playlist (boost::shared_ptr playlist) } } + if (unused) { + playlist->release(); + } + set_dirty(); PlaylistAdded (playlist); /* EMIT SIGNAL */ diff --git a/libs/ardour/tempo_map_importer.cc b/libs/ardour/tempo_map_importer.cc new file mode 100644 index 0000000000..45f2c90dfa --- /dev/null +++ b/libs/ardour/tempo_map_importer.cc @@ -0,0 +1,108 @@ +/* + Copyright (C) 2008 Paul Davis + Author: Sakari Bergen + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include + +#include + +#include +#include +#include +#include + +#include "i18n.h" + +using namespace PBD; +using namespace ARDOUR; + +/**** Handler ***/ +TempoMapImportHandler::TempoMapImportHandler (XMLTree const & source, Session & session) : + ElementImportHandler (source, session) +{ + XMLNode const * root = source.root(); + XMLNode const * tempo_map; + + if (!(tempo_map = root->child (X_("TempoMap")))) { + throw failed_constructor(); + } + + elements.push_back (ElementPtr ( new TempoMapImporter (source, session, *tempo_map))); +} + +string +TempoMapImportHandler::get_info () const +{ + return _("Tempo map"); +} + +/*** TempoMapImporter ***/ +TempoMapImporter::TempoMapImporter (XMLTree const & source, Session & session, XMLNode const & node) : + ElementImporter (source, session), + xml_tempo_map (node) +{ + name = _("Tempo Map"); +} + +string +TempoMapImporter::get_info () const +{ + std::ostringstream oss; + unsigned int tempos = 0; + unsigned int meters = 0; + XMLNodeList children = xml_tempo_map.children(); + + for (XMLNodeIterator it = children.begin(); it != children.end(); it++) { + if ((*it)->name() == "Tempo") { + tempos++; + } else if ((*it)->name() == "Meters") { + meters++; + } + } + + // return info + oss << _("Tempo marks: ") << tempos << _("\nMeter marks: ") << meters; + + return oss.str(); +} + +bool +TempoMapImporter::prepare_move () +{ + // Prompt user for verification + bool replace = Prompt (_("This will replace the current tempo map!\nAre you shure you want to do this?")); + + if (replace) { + queued = true; + } + + return replace; +} + +void +TempoMapImporter::cancel_move () +{ + queued = false; +} + +void +TempoMapImporter::move () +{ + session.tempo_map().set_state (xml_tempo_map); +} -- 2.30.2