fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / audio_track_importer.cc
index 752203f2cb79186cdb8374bcf4903bfd28110625..dd77cb370e4cce491e9a17a425f0801d6df6bd07 100644 (file)
 
 */
 
-#include <ardour/audio_track_importer.h>
+#include "ardour/audio_track_importer.h"
 
-#include <pbd/id.h>
-#include <pbd/failed_constructor.h>
+#include "ardour/audio_playlist_importer.h"
+#include "ardour/audio_diskstream.h"
+#include "ardour/session.h"
 
-#include "i18n.h"
+#include "pbd/controllable.h"
+#include "pbd/convert.h"
+#include "pbd/failed_constructor.h"
 
-namespace ARDOUR {
+#include <sstream>
+#include <algorithm>
+
+#include "pbd/i18n.h"
+
+using namespace std;
+using namespace PBD;
+using namespace ARDOUR;
 
 /*** AudioTrackImportHandler ***/
 
-AudioTrackImportHandler::AudioTrackImportHandler (XMLTree const & source, Session & session) :
+AudioTrackImportHandler::AudioTrackImportHandler (XMLTree const & source, Session & session, AudioPlaylistImportHandler & pl_handler) :
   ElementImportHandler (source, session)
 {
        XMLNode const * root = source.root();
        XMLNode const * routes;
-       
+
        if (!(routes = root->child ("Routes"))) {
                throw failed_constructor();
        }
-       
+
        XMLNodeList const & route_list = routes->children();
        for (XMLNodeList::const_iterator it = route_list.begin(); it != route_list.end(); ++it) {
-               const XMLProperty* type = (*it)->property("default-type");
-               if ( !type || type->value() == "audio" ) {
+               XMLProperty const * type = (*it)->property("default-type");
+               if ( (!type || type->value() == "audio") &&  ((*it)->property ("diskstream") != 0 || (*it)->property ("diskstream-id") != 0)) {
                        try {
-                               elements.push_back (ElementPtr ( new AudioTrackImporter (source, session, *this, **it)));
+                               elements.push_back (ElementPtr ( new AudioTrackImporter (source, session, *this, **it, pl_handler)));
                        } catch (failed_constructor err) {
                                set_dirty();
                        }
@@ -61,23 +71,148 @@ AudioTrackImportHandler::get_info () const
 
 /*** AudioTrackImporter ***/
 
-AudioTrackImporter::AudioTrackImporter (XMLTree const & source, Session & session, AudioTrackImportHandler & handler, XMLNode const & node) :
+AudioTrackImporter::AudioTrackImporter (XMLTree const & source,
+                                        Session & session,
+                                        AudioTrackImportHandler & track_handler,
+                                        XMLNode const & node,
+                                        AudioPlaylistImportHandler & pl_handler) :
   ElementImporter (source, session),
-  xml_track ("Route")
+  track_handler (track_handler),
+  xml_track (node),
+  pl_handler (pl_handler)
 {
-       // TODO Parse top-level XML
-       
-       if (!parse_io (node)) {
+       XMLProperty * prop;
+
+       if (!parse_route_xml ()) {
+               throw failed_constructor();
+       }
+
+       if (!parse_io ()) {
                throw failed_constructor();
        }
-       
-       XMLNodeList const & controllables = node.children ("controllable");
+
+       XMLNodeList const & controllables = node.children (Controllable::xml_node_name);
        for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
-               parse_controllable (**it, xml_track);
+               parse_controllable (**it);
+       }
+
+       XMLNode * remote_control = xml_track.child ("RemoteControl");
+       if (remote_control && (prop = remote_control->property ("id"))) {
+               uint32_t control_id = session.ntracks() + session.nbusses() + 1;
+               prop->set_value (to_string (control_id, std::dec));
        }
-       
-       // TODO parse remote-control and extra?
-       
+
+       xml_track.remove_nodes_and_delete ("Extra");
+}
+
+AudioTrackImporter::~AudioTrackImporter ()
+{
+       playlists.clear ();
+}
+
+bool
+AudioTrackImporter::parse_route_xml ()
+{
+       bool ds_ok = false;
+
+       // Remove order keys, new ones will be generated
+       xml_track.remove_property ("order-keys");
+
+       XMLPropertyList const & props = xml_track.properties();
+       for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
+               string prop = (*it)->name();
+               if (!prop.compare ("default-type") || !prop.compare ("flags") ||
+                 !prop.compare ("active") || !prop.compare ("muted") ||
+                 !prop.compare ("soloed") || !prop.compare ("phase-invert") ||
+                 !prop.compare ("denormal-protection") || !prop.compare("mute-affects-pre-fader") ||
+                 !prop.compare ("mute-affects-post-fader") || !prop.compare("mute-affects-control-outs") ||
+                 !prop.compare ("mute-affects-main-outs") || !prop.compare("mode")) {
+                       // All ok
+               } else if (!prop.compare("diskstream-id")) {
+                       old_ds_id = (*it)->value();
+                       (*it)->set_value (new_ds_id.to_s());
+                       ds_ok = true;
+               } else {
+                       std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
+               }
+       }
+
+       if (!ds_ok) {
+               error << X_("AudioTrackImporter: did not find necessary XML-property \"diskstream-id\"") << endmsg;
+               return false;
+       }
+
+       return true;
+}
+
+bool
+AudioTrackImporter::parse_io ()
+{
+       XMLNode * io;
+       bool name_ok = false;
+       bool id_ok = false;
+
+       if (!(io = xml_track.child ("IO"))) {
+               return false;
+       }
+
+       XMLPropertyList const & props = io->properties();
+
+       for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
+               string prop = (*it)->name();
+               if (!prop.compare ("gain") || !prop.compare ("iolimits")) {
+                       // All ok
+               } else if (!prop.compare("name")) {
+                       name = (*it)->value();
+                       name_ok = true;
+               } else if (!prop.compare("id")) {
+                       PBD::ID id;
+                       (*it)->set_value (id.to_s());
+                       id_ok = true;
+               } else if (!prop.compare("inputs")) {
+                       // TODO Handle this properly!
+                       /* Input and output ports are counted and added empty, so that no in/output connecting function fails. */
+                       uint32_t num_inputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
+                       std::string value;
+                       for (uint32_t i = 0; i < num_inputs; i++) { value += "{}"; }
+                       (*it)->set_value (value);
+               } else if (!prop.compare("outputs")) {
+                       // TODO See comments above
+                       uint32_t num_outputs = std::count ((*it)->value().begin(), (*it)->value().end(), '{');
+                       std::string value;
+                       for (uint32_t i = 0; i < num_outputs; i++) { value += "{}"; }
+                       (*it)->set_value (value);
+               } else {
+                       std::cerr << string_compose (X_("AudioTrackImporter: did not recognise XML-property \"%1\""), prop) << endmsg;
+               }
+       }
+
+       if (!name_ok) {
+               error << X_("AudioTrackImporter: did not find necessary XML-property \"name\"") << endmsg;
+               return false;
+       }
+
+       if (!id_ok) {
+               error << X_("AudioTrackImporter: did not find necessary XML-property \"id\"") << endmsg;
+               return false;
+       }
+
+       XMLNodeList const & controllables = io->children (Controllable::xml_node_name);
+       for (XMLNodeList::const_iterator it = controllables.begin(); it != controllables.end(); ++it) {
+               parse_controllable (**it);
+       }
+
+       XMLNodeList const & processors = io->children ("Processor");
+       for (XMLNodeList::const_iterator it = processors.begin(); it != processors.end(); ++it) {
+               parse_processor (**it);
+       }
+
+       XMLNodeList const & automations = io->children ("Automation");
+       for (XMLNodeList::const_iterator it = automations.begin(); it != automations.end(); ++it) {
+               parse_automation (**it);
+       }
+
+       return true;
 }
 
 string
@@ -87,66 +222,182 @@ AudioTrackImporter::get_info () const
        return name;
 }
 
+/** @return true if everything is ok */
 bool
-AudioTrackImporter::prepare_move ()
+AudioTrackImporter::_prepare_move ()
 {
-       // TODO
-       return false;
+       /* Copy dependent playlists */
+
+       pl_handler.playlists_by_diskstream (old_ds_id, playlists);
+
+       for (PlaylistList::iterator it = playlists.begin(); it != playlists.end(); ++it) {
+               if (!(*it)->prepare_move ()) {
+                       playlists.clear ();
+                       return false;
+               }
+               (*it)->set_diskstream (new_ds_id);
+       }
+
+       /* Rename */
+
+       while (session.route_by_name (name) || !track_handler.check_name (name)) {
+               std::pair<bool, string> rename_pair = *Rename (_("A playlist with this name already exists, please rename it."), name);
+               if (!rename_pair.first) {
+                       return false;
+               }
+               name = rename_pair.second;
+       }
+
+       XMLNode* c = xml_track.child ("IO");
+       if (!c) {
+               error << _("badly-formed XML in imported track") << endmsg;
+               return false;
+       }
+
+       XMLProperty * p = c->property ("name");
+       if (!p) {
+               error << _("badly-formed XML in imported track") << endmsg;
+               return false;
+       }
+
+       p->set_value (name);
+
+       track_handler.add_name (name);
+
+       return true;
 }
 
 void
-AudioTrackImporter::cancel_move ()
+AudioTrackImporter::_cancel_move ()
 {
-       // TODO
+       track_handler.remove_name (name);
+       playlists.clear ();
 }
 
 void
-AudioTrackImporter::move ()
+AudioTrackImporter::_move ()
 {
-       // TODO
+       /* Add diskstream */
+
+       boost::shared_ptr<XMLSharedNodeList> ds_node_list;
+       string xpath = "/Session/DiskStreams/AudioDiskstream[@id='" + old_ds_id.to_s() + "']";
+       ds_node_list = source.find (xpath);
+
+       if (ds_node_list->size() != 1) {
+               error << string_compose (_("Error Importing Audio track %1"), name) << endmsg;
+               return;
+       }
+
+       boost::shared_ptr<XMLNode> ds_node = ds_node_list->front();
+       XMLProperty * p = ds_node->property (X_("id"));
+       assert (p);
+       p->set_value (new_ds_id.to_s());
+
+       boost::shared_ptr<Diskstream> new_ds (new AudioDiskstream (session, *ds_node));
+       new_ds->set_name (name);
+       new_ds->do_refill_with_alloc ();
+       new_ds->set_block_size (session.get_block_size ());
+
+       /* Import playlists */
+
+       for (PlaylistList::const_iterator it = playlists.begin(); it != playlists.end(); ++it) {
+               (*it)->move ();
+       }
+
+       /* Import track */
+
+       XMLNode routes ("Routes");
+       routes.add_child_copy (xml_track);
+       session.load_routes (routes, 3000);
 }
 
 bool
-AudioTrackImporter::parse_io (XMLNode const & node)
+AudioTrackImporter::parse_processor (XMLNode & node)
 {
-       XMLNode * io;
-       XMLProperty * prop;
-
-       if (!(io = node.child ("IO"))) {
-               return false;
-       }
-       
-       if ((prop = io->property ("name"))) {
-               name = prop->value();
-       } else {
-               return false;
+       XMLNode * automation = node.child ("Automation");
+       if (automation) {
+               parse_automation (*automation);
        }
-       
-       // TODO parse rest of the XML
 
        return true;
 }
 
 bool
-AudioTrackImporter::parse_controllable (XMLNode const & node, XMLNode & dest_parent)
+AudioTrackImporter::parse_controllable (XMLNode & node)
 {
        XMLProperty * prop;
-       XMLNode new_node (node);
-       
-       if ((prop = new_node.property ("id"))) {
-               PBD::ID old_id (prop->value());
+
+       if ((prop = node.property ("id"))) {
                PBD::ID new_id;
-               
                prop->set_value (new_id.to_s());
-               // TODO do id mapping and everything else necessary...
-               
        } else {
                return false;
        }
-       
-       dest_parent.add_child_copy (new_node);
 
        return true;
 }
 
-} // namespace ARDOUR
+bool
+AudioTrackImporter::parse_automation (XMLNode & node)
+{
+
+       XMLNodeList const & lists = node.children ("AutomationList");
+       for (XMLNodeList::const_iterator it = lists.begin(); it != lists.end(); ++it) {
+               XMLProperty * prop;
+
+               if ((prop = (*it)->property ("id"))) {
+                       PBD::ID id;
+                       prop->set_value (id.to_s());
+               }
+
+               if (!(*it)->name().compare ("events")) {
+                       rate_convert_events (**it);
+               }
+       }
+
+       return true;
+}
+
+bool
+AudioTrackImporter::rate_convert_events (XMLNode & node)
+{
+       if (node.children().empty()) {
+               return false;
+       }
+
+       XMLNode* content_node = node.children().front();
+
+       if (content_node->content().empty()) {
+               return false;
+       }
+
+       std::stringstream str (content_node->content());
+       std::ostringstream new_content;
+
+       framecnt_t x;
+       double y;
+       bool ok = true;
+
+       while (str) {
+               str >> x;
+               if (!str) {
+                       break;
+               }
+               str >> y;
+               if (!str) {
+                       ok = false;
+                       break;
+               }
+
+               new_content << rate_convert_samples (x) << ' ' << y;
+       }
+
+       if (!ok) {
+               error << X_("AudioTrackImporter: error in rate converting automation events") << endmsg;
+               return false;
+       }
+
+       content_node->set_content (new_content.str());
+
+       return true;
+}