more fun and games with latency compensation, and so forth ... not done yet
[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 "i18n.h"
31
32 using namespace std;
33 using namespace PBD;
34 using namespace ARDOUR;
35
36 SessionPlaylists::~SessionPlaylists ()
37 {
38         DEBUG_TRACE (DEBUG::Destruction, "delete playlists\n");
39         
40         for (List::iterator i = playlists.begin(); i != playlists.end(); ) {
41                 SessionPlaylists::List::iterator tmp;
42
43                 tmp = i;
44                 ++tmp;
45
46                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
47                 boost::shared_ptr<Playlist> keeper (*i);
48                 (*i)->drop_references ();
49
50                 i = tmp;
51         }
52
53         DEBUG_TRACE (DEBUG::Destruction, "delete unused playlists\n");
54         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) {
55                 List::iterator tmp;
56
57                 tmp = i;
58                 ++tmp;
59
60                 DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
61                 boost::shared_ptr<Playlist> keeper (*i);
62                 (*i)->drop_references ();
63
64                 i = tmp;
65         }
66
67         playlists.clear ();
68         unused_playlists.clear ();
69 }
70
71 bool
72 SessionPlaylists::add (boost::shared_ptr<Playlist> playlist)
73 {
74         Glib::Mutex::Lock lm (lock);
75
76         bool const existing = find (playlists.begin(), playlists.end(), playlist) != playlists.end();
77
78         if (!existing) {
79                 playlists.insert (playlists.begin(), playlist);
80                 playlist->InUse.connect_same_thread (*this, boost::bind (&SessionPlaylists::track, this, _1, boost::weak_ptr<Playlist>(playlist)));
81         }
82
83         return existing;
84 }
85
86 void
87 SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
88 {
89         Glib::Mutex::Lock lm (lock);
90
91         List::iterator i;
92
93         i = find (playlists.begin(), playlists.end(), playlist);
94         if (i != playlists.end()) {
95                 playlists.erase (i);
96         }
97
98         i = find (unused_playlists.begin(), unused_playlists.end(), playlist);
99         if (i != unused_playlists.end()) {
100                 unused_playlists.erase (i);
101         }
102 }
103         
104
105 void
106 SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
107 {
108         boost::shared_ptr<Playlist> pl(wpl.lock());
109
110         if (!pl) {
111                 return;
112         }
113
114         List::iterator x;
115
116         if (pl->hidden()) {
117                 /* its not supposed to be visible */
118                 return;
119         }
120
121         {
122                 Glib::Mutex::Lock lm (lock);
123
124                 if (!inuse) {
125
126                         unused_playlists.insert (pl);
127
128                         if ((x = playlists.find (pl)) != playlists.end()) {
129                                 playlists.erase (x);
130                         }
131
132
133                 } else {
134
135                         playlists.insert (pl);
136
137                         if ((x = unused_playlists.find (pl)) != unused_playlists.end()) {
138                                 unused_playlists.erase (x);
139                         }
140                 }
141         }
142 }
143
144 uint32_t
145 SessionPlaylists::n_playlists () const
146 {
147         Glib::Mutex::Lock lm (lock);
148         return playlists.size();
149 }
150
151 boost::shared_ptr<Playlist>
152 SessionPlaylists::by_name (string name)
153 {
154         Glib::Mutex::Lock lm (lock);
155
156         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
157                 if ((*i)->name() == name) {
158                         return* i;
159                 }
160         }
161         
162         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
163                 if ((*i)->name() == name) {
164                         return* i;
165                 }
166         }
167
168         return boost::shared_ptr<Playlist>();
169 }
170
171 boost::shared_ptr<Playlist>
172 SessionPlaylists::by_id (const PBD::ID& id)
173 {
174         Glib::Mutex::Lock lm (lock);
175
176         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
177                 if ((*i)->id() == id) {
178                         return* i;
179                 }
180         }
181         
182         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
183                 if ((*i)->id() == id) {
184                         return* i;
185                 }
186         }
187
188         return boost::shared_ptr<Playlist>();
189 }
190
191 void
192 SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & list)
193 {
194         Glib::Mutex::Lock lm (lock);
195
196         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
197                 if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
198                         list.push_back (*i);
199                 }
200         }
201         
202         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
203                 if (!(*i)->get_orig_diskstream_id().to_s().compare ("0")) {
204                         list.push_back (*i);
205                 }
206         }
207 }
208
209 void
210 SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s)
211 {
212         Glib::Mutex::Lock lm (lock);
213
214         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
215                 s.push_back (*i);
216         }
217         
218         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
219                 s.push_back (*i);
220         }
221 }
222
223 void
224 SessionPlaylists::destroy_region (boost::shared_ptr<Region> r)
225 {
226         Glib::Mutex::Lock lm (lock);
227
228         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
229                 (*i)->destroy_region (r);
230         }
231         
232         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
233                 (*i)->destroy_region (r);
234         }
235 }
236
237
238 void
239 SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
240 {
241         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i)
242                 (*i)->get_region_list_equivalent_regions (region, result);
243 }
244
245 /** Return the number of playlists (not regions) that contain @a src */
246 uint32_t
247 SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
248 {
249         uint32_t count = 0;
250
251         for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
252                 if ((*p)->uses_source (src)) {
253                         ++count;
254                         break;
255                 }
256         }
257         return count;
258 }
259
260 void
261 SessionPlaylists::sync_all_regions_with_regions ()
262 {
263         Glib::Mutex::Lock lm (lock);
264
265         for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
266                 (*p)->sync_all_regions_with_regions ();
267         }
268 }
269
270 void
271 SessionPlaylists::update_after_tempo_map_change ()
272 {
273         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
274                 (*i)->update_after_tempo_map_change ();
275         }
276
277         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
278                 (*i)->update_after_tempo_map_change ();
279         }
280 }
281
282 void
283 SessionPlaylists::add_state (XMLNode* node, bool full_state)
284 {
285         XMLNode* child = node->add_child ("Playlists");
286         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
287                 if (!(*i)->hidden()) {
288                         if (full_state) {
289                                 child->add_child_nocopy ((*i)->get_state());
290                         } else {
291                                 child->add_child_nocopy ((*i)->get_template());
292                         }
293                 }
294         }
295
296         child = node->add_child ("UnusedPlaylists");
297         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
298                 if (!(*i)->hidden()) {
299                         if (!(*i)->empty()) {
300                                 if (full_state) {
301                                         child->add_child_nocopy ((*i)->get_state());
302                                 } else {
303                                         child->add_child_nocopy ((*i)->get_template());
304                                 }
305                         }
306                 }
307         }
308 }
309
310 /** @return true for `stop cleanup', otherwise false */
311 bool
312 SessionPlaylists::maybe_delete_unused (boost::function<int(boost::shared_ptr<Playlist>)> ask)
313 {
314         vector<boost::shared_ptr<Playlist> > playlists_tbd;
315
316         for (List::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
317
318                 int status = ask (*x);
319
320                 switch (status) {
321                 case -1:
322                         return true;
323
324                 case 0:
325                         playlists_tbd.push_back (*x);
326                         break;
327
328                 default:
329                         /* leave it alone */
330                         break;
331                 }
332         }
333
334         /* now delete any that were marked for deletion */
335
336         for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
337                 boost::shared_ptr<Playlist> keeper (*x);
338                 (*x)->drop_references ();
339         }
340
341         playlists_tbd.clear ();
342
343         return false;
344 }
345
346 int
347 SessionPlaylists::load (Session& session, const XMLNode& node)
348 {
349         XMLNodeList nlist;
350         XMLNodeConstIterator niter;
351         boost::shared_ptr<Playlist> playlist;
352
353         nlist = node.children();
354
355         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
356
357                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
358                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
359                 }
360         }
361
362         return 0;
363 }
364
365 int
366 SessionPlaylists::load_unused (Session& session, const XMLNode& node)
367 {
368         XMLNodeList nlist;
369         XMLNodeConstIterator niter;
370         boost::shared_ptr<Playlist> playlist;
371
372         nlist = node.children();
373
374         for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
375
376                 if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
377                         error << _("Session: cannot create Playlist from XML description.") << endmsg;
378                         continue;
379                 }
380
381                 // now manually untrack it
382
383                 track (false, boost::weak_ptr<Playlist> (playlist));
384         }
385
386         return 0;
387 }
388
389 boost::shared_ptr<Playlist>
390 SessionPlaylists::XMLPlaylistFactory (Session& session, const XMLNode& node)
391 {
392         try {
393                 return PlaylistFactory::create (session, node);
394         }
395
396         catch (failed_constructor& err) {
397                 return boost::shared_ptr<Playlist>();
398         }
399 }
400
401 boost::shared_ptr<Crossfade>
402 SessionPlaylists::find_crossfade (const PBD::ID& id)
403 {
404         Glib::Mutex::Lock lm (lock);
405
406         boost::shared_ptr<Crossfade> c;
407         
408         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
409                 c = (*i)->find_crossfade (id);
410                 if (c) {
411                         return c;
412                 }
413         }
414
415         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
416                 c = (*i)->find_crossfade (id);
417                 if (c) {
418                         return c;
419                 }
420         }
421
422         return boost::shared_ptr<Crossfade> ();
423 }
424
425 uint32_t
426 SessionPlaylists::region_use_count (boost::shared_ptr<Region> region) const
427 {
428         Glib::Mutex::Lock lm (lock);
429         uint32_t cnt = 0;
430         
431         for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
432                 cnt += (*i)->region_use_count (region);
433         }
434
435         for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
436                 cnt += (*i)->region_use_count (region);
437         }
438
439         return cnt;
440 }