fix a few dangling uses of Ardour as program name, in favor of PROGRAM_NAME
[ardour.git] / libs / ardour / session_playlists.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19 #include <vector>
20
21 #include "pbd/xml++.h"
22 #include "pbd/compose.h"
23 #include "ardour/debug.h"
24 #include "ardour/session_playlists.h"
25 #include "ardour/playlist.h"
26 #include "ardour/region.h"
27 #include "ardour/playlist_factory.h"
28 #include "ardour/session.h"
29 #include "ardour/source.h"
30 #include "ardour/track.h"
31 #include "i18n.h"
32
33 using namespace std;
34 using namespace PBD;
35 using namespace ARDOUR;
36
37 SessionPlaylists::~SessionPlaylists ()
38 {
39         DEBUG_TRACE (DEBUG::Destruction, "delete playlists\n");
40
41         for (List::iterator i = playlists.begin(); i != playlists.end(); ) {
42                 SessionPlaylists::List::iterator tmp;
43
44                 tmp = i;
45                 ++tmp;
46
47                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
48                 boost::shared_ptr<Playlist> keeper (*i);
49                 (*i)->drop_references ();
50
51                 i = tmp;
52         }
53
54         DEBUG_TRACE (DEBUG::Destruction, "delete unused playlists\n");
55         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) {
56                 List::iterator tmp;
57
58                 tmp = i;
59                 ++tmp;
60
61                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
62                 boost::shared_ptr<Playlist> keeper (*i);
63                 (*i)->drop_references ();
64
65                 i = tmp;
66         }
67
68         playlists.clear ();
69         unused_playlists.clear ();
70 }
71
72 bool
73 SessionPlaylists::add (boost::shared_ptr<Playlist> playlist)
74 {
75         Glib::Mutex::Lock lm (lock);
76
77         bool const existing = find (playlists.begin(), playlists.end(), playlist) != playlists.end();
78
79         if (!existing) {
80                 playlists.insert (playlists.begin(), playlist);
81                 playlist->InUse.connect_same_thread (*this, boost::bind (&SessionPlaylists::track, this, _1, boost::weak_ptr<Playlist>(playlist)));
82                 playlist->DropReferences.connect_same_thread (
83                         *this, boost::bind (&SessionPlaylists::remove_weak, this, boost::weak_ptr<Playlist> (playlist))
84                         );
85         }
86
87         return existing;
88 }
89
90 void
91 SessionPlaylists::remove_weak (boost::weak_ptr<Playlist> playlist)
92 {
93         boost::shared_ptr<Playlist> p = playlist.lock ();
94         if (p) {
95                 remove (p);
96         }
97 }
98
99 void
100 SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
101 {
102         Glib::Mutex::Lock lm (lock);
103
104         List::iterator i;
105
106         i = find (playlists.begin(), playlists.end(), playlist);
107         if (i != playlists.end()) {
108                 playlists.erase (i);
109         }
110
111         i = find (unused_playlists.begin(), unused_playlists.end(), playlist);
112         if (i != unused_playlists.end()) {
113                 unused_playlists.erase (i);
114         }
115 }
116
117
118 void
119 SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
120 {
121         boost::shared_ptr<Playlist> pl(wpl.lock());
122
123         if (!pl) {
124                 return;
125         }
126
127         List::iterator x;
128
129         if (pl->hidden()) {
130                 /* its not supposed to be visible */
131                 return;
132         }
133
134         {
135                 Glib::Mutex::Lock lm (lock);
136
137                 if (!inuse) {
138
139                         unused_playlists.insert (pl);
140
141                         if ((x = playlists.find (pl)) != playlists.end()) {
142                                 playlists.erase (x);
143                         }
144
145
146                 } else {
147
148                         playlists.insert (pl);
149
150                         if ((x = unused_playlists.find (pl)) != unused_playlists.end()) {
151                                 unused_playlists.erase (x);
152                         }
153                 }
154         }
155 }
156
157 uint32_t
158 SessionPlaylists::n_playlists () const
159 {
160         Glib::Mutex::Lock lm (lock);
161         return playlists.size();
162 }
163
164 boost::shared_ptr<Playlist>
165 SessionPlaylists::by_name (string name)
166 {
167         Glib::Mutex::Lock lm (lock);
168
169         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
170                 if ((*i)->name() == name) {
171                         return* i;
172                 }
173         }
174
175         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
176                 if ((*i)->name() == name) {
177                         return* i;
178                 }
179         }
180
181         return boost::shared_ptr<Playlist>();
182 }
183
184 boost::shared_ptr<Playlist>
185 SessionPlaylists::by_id (const PBD::ID& id)
186 {
187         Glib::Mutex::Lock lm (lock);
188
189         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
190                 if ((*i)->id() == id) {
191                         return* i;
192                 }
193         }
194
195         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
196                 if ((*i)->id() == id) {
197                         return* i;
198                 }
199         }
200
201         return boost::shared_ptr<Playlist>();
202 }
203
204 void
205 SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & list)
206 {
207         Glib::Mutex::Lock lm (lock);
208
209         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
210                 if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
211                         list.push_back (*i);
212                 }
213         }
214
215         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
216                 if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
217                         list.push_back (*i);
218                 }
219         }
220 }
221
222 void
223 SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s) const
224 {
225         Glib::Mutex::Lock lm (lock);
226
227         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
228                 s.push_back (*i);
229         }
230
231         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
232                 s.push_back (*i);
233         }
234 }
235
236 void
237 SessionPlaylists::destroy_region (boost::shared_ptr<Region> r)
238 {
239         Glib::Mutex::Lock lm (lock);
240
241         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
242                 (*i)->destroy_region (r);
243         }
244
245         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
246                 (*i)->destroy_region (r);
247         }
248 }
249
250
251 void
252 SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
253 {
254         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i)
255                 (*i)->get_region_list_equivalent_regions (region, result);
256 }
257
258 /** Return the number of playlists (not regions) that contain @a src */
259 uint32_t
260 SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
261 {
262         uint32_t count = 0;
263
264         for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
265                 if ((*p)->uses_source (src)) {
266                         ++count;
267                         break;
268                 }
269         }
270         return count;
271 }
272
273 void
274 SessionPlaylists::sync_all_regions_with_regions ()
275 {
276         Glib::Mutex::Lock lm (lock);
277
278         for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
279                 (*p)->sync_all_regions_with_regions ();
280         }
281 }
282
283 void
284 SessionPlaylists::update_after_tempo_map_change ()
285 {
286         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
287                 (*i)->update_after_tempo_map_change ();
288         }
289
290         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
291                 (*i)->update_after_tempo_map_change ();
292         }
293 }
294
295 void
296 SessionPlaylists::add_state (XMLNode* node, bool full_state)
297 {
298         XMLNode* child = node->add_child ("Playlists");
299         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
300                 if (!(*i)->hidden()) {
301                         if (full_state) {
302                                 child->add_child_nocopy ((*i)->get_state());
303                         } else {
304                                 child->add_child_nocopy ((*i)->get_template());
305                         }
306                 }
307         }
308
309         child = node->add_child ("UnusedPlaylists");
310         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
311                 if (!(*i)->hidden()) {
312                         if (!(*i)->empty()) {
313                                 if (full_state) {
314                                         child->add_child_nocopy ((*i)->get_state());
315                                 } else {
316                                         child->add_child_nocopy ((*i)->get_template());
317                                 }
318                         }
319                 }
320         }
321 }
322
323 /** @return true for `stop cleanup', otherwise false */
324 bool
325 SessionPlaylists::maybe_delete_unused (boost::function<int(boost::shared_ptr<Playlist>)> ask)
326 {
327         vector<boost::shared_ptr<Playlist> > playlists_tbd;
328
329         for (List::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
330
331                 int status = ask (*x);
332
333                 switch (status) {
334                 case -1:
335                         return true;
336
337                 case 0:
338                         playlists_tbd.push_back (*x);
339                         break;
340
341                 default:
342                         /* leave it alone */
343                         break;
344                 }
345         }
346
347         /* now delete any that were marked for deletion */
348
349         for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
350                 boost::shared_ptr<Playlist> keeper (*x);
351                 (*x)->drop_references ();
352         }
353
354         playlists_tbd.clear ();
355
356         return false;
357 }
358
359 int
360 SessionPlaylists::load (Session& session, const XMLNode& node)
361 {
362         XMLNodeList nlist;
363         XMLNodeConstIterator niter;
364         boost::shared_ptr<Playlist> playlist;
365
366         nlist = node.children();
367
368         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
369
370                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
371                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
372                 }
373         }
374
375         return 0;
376 }
377
378 int
379 SessionPlaylists::load_unused (Session& session, const XMLNode& node)
380 {
381         XMLNodeList nlist;
382         XMLNodeConstIterator niter;
383         boost::shared_ptr<Playlist> playlist;
384
385         nlist = node.children();
386
387         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
388
389                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
390                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
391                         continue;
392                 }
393
394                 // now manually untrack it
395
396                 track (false, boost::weak_ptr<Playlist> (playlist));
397         }
398
399         return 0;
400 }
401
402 boost::shared_ptr<Playlist>
403 SessionPlaylists::XMLPlaylistFactory (Session& session, const XMLNode& node)
404 {
405         try {
406                 return PlaylistFactory::create (session, node);
407         }
408
409         catch (failed_constructor& err) {
410                 return boost::shared_ptr<Playlist>();
411         }
412 }
413
414 boost::shared_ptr<Crossfade>
415 SessionPlaylists::find_crossfade (const PBD::ID& id)
416 {
417         Glib::Mutex::Lock lm (lock);
418
419         boost::shared_ptr<Crossfade> c;
420
421         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
422                 c = (*i)->find_crossfade (id);
423                 if (c) {
424                         return c;
425                 }
426         }
427
428         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
429                 c = (*i)->find_crossfade (id);
430                 if (c) {
431                         return c;
432                 }
433         }
434
435         return boost::shared_ptr<Crossfade> ();
436 }
437
438 uint32_t
439 SessionPlaylists::region_use_count (boost::shared_ptr<Region> region) const
440 {
441         Glib::Mutex::Lock lm (lock);
442         uint32_t cnt = 0;
443
444         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
445                 cnt += (*i)->region_use_count (region);
446         }
447
448         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
449                 cnt += (*i)->region_use_count (region);
450         }
451
452         return cnt;
453 }
454
455 /** @return list of Playlists that are associated with a track */
456 vector<boost::shared_ptr<Playlist> >
457 SessionPlaylists::playlists_for_track (boost::shared_ptr<Track> tr) const
458 {
459         vector<boost::shared_ptr<Playlist> > pl;
460         get (pl);
461         
462         vector<boost::shared_ptr<Playlist> > pl_tr;
463
464         for (vector<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
465                 if (((*i)->get_orig_track_id() == tr->id()) || (tr->playlist()->id() == (*i)->id())) {
466                         pl_tr.push_back (*i);
467                 }
468         }
469
470         return pl_tr;
471 }