start removal of NoteFixer code
[ardour.git] / libs / ardour / audio_playlist_importer.cc
1 /*
2  * Copyright (C) 2008-2014 David Robillard <d@drobilla.net>
3  * Copyright (C) 2008 Sakari Bergen <sakari.bergen@beatwaves.net>
4  * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5  * Copyright (C) 2009-2016 Paul Davis <paul@linuxaudiosystems.com>
6  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "ardour/audio_playlist_importer.h"
24
25 #include <sstream>
26
27 #include "pbd/failed_constructor.h"
28 #include "pbd/compose.h"
29 #include "pbd/error.h"
30
31 #include "ardour/audio_region_importer.h"
32 #include "ardour/session.h"
33 #include "ardour/playlist_factory.h"
34 #include "ardour/session_playlists.h"
35
36 #include "pbd/i18n.h"
37
38 using namespace std;
39 using namespace PBD;
40 using namespace ARDOUR;
41
42 /**** Handler ***/
43 AudioPlaylistImportHandler::AudioPlaylistImportHandler (XMLTree const & source, Session & session, AudioRegionImportHandler & region_handler, const char * nodename) :
44   ElementImportHandler (source, session),
45   region_handler (region_handler)
46 {
47         XMLNode const * root = source.root();
48         XMLNode const * playlists;
49
50         if (!(playlists = root->child (nodename))) {
51                 throw failed_constructor();
52         }
53
54         XMLNodeList const & pl_children = playlists->children();
55         for (XMLNodeList::const_iterator it = pl_children.begin(); it != pl_children.end(); ++it) {
56                 XMLProperty const * type = (*it)->property("type");
57                 if ( !type || type->value() == "audio" ) {
58                         try {
59                                 elements.push_back (ElementPtr ( new AudioPlaylistImporter (source, session, *this, **it)));
60                         } catch (failed_constructor const&) {
61                                 set_dirty();
62                         }
63                 }
64         }
65 }
66
67 string
68 AudioPlaylistImportHandler::get_info () const
69 {
70         return _("Audio Playlists");
71 }
72
73 void
74 AudioPlaylistImportHandler::get_regions (XMLNode const & node, ElementList & list) const
75 {
76         region_handler.create_regions_from_children (node, list);
77 }
78
79 void
80 AudioPlaylistImportHandler::update_region_id (XMLProperty * id_prop)
81 {
82         PBD::ID old_id (id_prop->value());
83         PBD::ID new_id (region_handler.get_new_id (old_id));
84         id_prop->set_value (new_id.to_s());
85 }
86
87 void
88 AudioPlaylistImportHandler::playlists_by_diskstream (PBD::ID const & id, PlaylistList & list) const
89 {
90         for (ElementList::const_iterator it = elements.begin(); it != elements.end(); ++it) {
91                 boost::shared_ptr<AudioPlaylistImporter> pl = boost::dynamic_pointer_cast<AudioPlaylistImporter> (*it);
92                 if (pl && pl->orig_diskstream() == id) {
93                         list.push_back (PlaylistPtr (new AudioPlaylistImporter (*pl)));
94                 }
95         }
96 }
97
98 /*** AudioPlaylistImporter ***/
99 AudioPlaylistImporter::AudioPlaylistImporter (XMLTree const & source, Session & session, AudioPlaylistImportHandler & handler, XMLNode const & node) :
100   ElementImporter (source, session),
101   handler (handler),
102   orig_node (node),
103   xml_playlist (node),
104   diskstream_id ("0")
105 {
106         bool ds_ok = false;
107
108         populate_region_list ();
109
110         // Parse XML
111         XMLPropertyList const & props = xml_playlist.properties();
112         for (XMLPropertyList::const_iterator it = props.begin(); it != props.end(); ++it) {
113                 string prop = (*it)->name();
114                 if (!prop.compare("type") || !prop.compare("frozen")) {
115                         // All ok
116                 } else if (!prop.compare("name")) {
117                         name = (*it)->value();
118                 } else if (!prop.compare("orig-diskstream-id")) {
119                         orig_diskstream_id = (*it)->value();
120                         ds_ok = true;
121                 } else {
122                         std::cerr << string_compose (X_("AudioPlaylistImporter did not recognise XML-property \"%1\""), prop) << endmsg;
123                 }
124         }
125
126         if (!ds_ok) {
127                 error << string_compose (X_("AudioPlaylistImporter (%1): did not find XML-property \"orig_diskstream_id\" which is mandatory"), name) << endmsg;
128                 throw failed_constructor();
129         }
130 }
131
132 AudioPlaylistImporter::AudioPlaylistImporter (AudioPlaylistImporter const & other) :
133   ElementImporter (other.source, other.session),
134   handler (other.handler),
135   orig_node (other.orig_node),
136   xml_playlist (other.xml_playlist),
137   orig_diskstream_id (other.orig_diskstream_id)
138 {
139         populate_region_list ();
140 }
141
142 AudioPlaylistImporter::~AudioPlaylistImporter ()
143 {
144
145 }
146
147 string
148 AudioPlaylistImporter::get_info () const
149 {
150         XMLNodeList children = xml_playlist.children();
151         unsigned int regions = 0;
152         std::ostringstream oss;
153
154         for (XMLNodeIterator it = children.begin(); it != children.end(); it++) {
155                 if ((*it)->name() == "Region") {
156                         ++regions;
157                 }
158         }
159
160         oss << regions << " ";
161
162         if (regions == 1) {
163                 oss << _("region");
164         } else {
165                 oss << _("regions");
166         }
167
168         return oss.str();
169 }
170
171 bool
172 AudioPlaylistImporter::_prepare_move ()
173 {
174         // Rename
175         while (session.playlists()->by_name (name) || !handler.check_name (name)) {
176                 std::pair<bool, string> rename_pair = *Rename (_("A playlist with this name already exists, please rename it."), name);
177                 if (!rename_pair.first) {
178                         return false;
179                 }
180                 name = rename_pair.second;
181         }
182
183         XMLProperty * p = xml_playlist.property ("name");
184         if (!p) {
185                 error << _("badly-formed XML in imported playlist") << endmsg;
186                 return false;
187         }
188
189         p->set_value (name);
190         handler.add_name (name);
191
192         return true;
193 }
194
195 void
196 AudioPlaylistImporter::_cancel_move ()
197 {
198         handler.remove_name (name);
199 }
200
201 void
202 AudioPlaylistImporter::_move ()
203 {
204         boost::shared_ptr<Playlist> playlist;
205
206         // Update diskstream id
207         xml_playlist.property ("orig-diskstream-id")->set_value (diskstream_id.to_s());
208
209         // Update region XML in playlist and prepare sources
210         xml_playlist.remove_nodes("Region");
211         for (RegionList::iterator it = regions.begin(); it != regions.end(); ++it) {
212                 xml_playlist.add_child_copy ((*it)->get_xml());
213                 (*it)->add_sources_to_session();
214                 if ((*it)->broken()) {
215                         handler.set_dirty();
216                         set_broken();
217                         return; // TODO clean up?
218                 }
219         }
220
221         // Update region ids in crossfades
222         XMLNodeList crossfades = xml_playlist.children("Crossfade");
223         for (XMLNodeIterator it = crossfades.begin(); it != crossfades.end(); ++it) {
224                 XMLProperty * in = (*it)->property("in");
225                 XMLProperty * out = (*it)->property("out");
226                 if (!in || !out) {
227                         error << string_compose (X_("AudioPlaylistImporter (%1): did not find the \"in\" or \"out\" property from a crossfade"), name) << endmsg;
228                         continue; // or fatal?
229                 }
230
231                 handler.update_region_id (in);
232                 handler.update_region_id (out);
233
234                 // rate convert length and position
235                 XMLProperty * length = (*it)->property("length");
236                 if (length) {
237                         length->set_value (rate_convert_samples (length->value()));
238                 }
239
240                 XMLProperty * position = (*it)->property("position");
241                 if (position) {
242                         position->set_value (rate_convert_samples (position->value()));
243                 }
244         }
245
246         // Create playlist
247         playlist = PlaylistFactory::create (session, xml_playlist, false, true);
248 }
249
250 void
251 AudioPlaylistImporter::set_diskstream (PBD::ID const & id)
252 {
253         diskstream_id = id;
254 }
255
256 void
257 AudioPlaylistImporter::populate_region_list ()
258 {
259         ElementImportHandler::ElementList elements;
260         handler.get_regions (orig_node, elements);
261         for (ElementImportHandler::ElementList::iterator it = elements.begin(); it != elements.end(); ++it) {
262                 regions.push_back (boost::dynamic_pointer_cast<AudioRegionImporter> (*it));
263         }
264 }
265
266 string
267 UnusedAudioPlaylistImportHandler::get_info () const
268 {
269         return _("Audio Playlists (unused)");
270 }