mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-12 09:36:33 +01:00
Actually, when duplicating a track with "share playlist", the current playlist is owned by the new created track(orig-track-id). The sharing mecanism is made by diskstreams pointing on the same(shared) playlist. Since playlist now owned by the new track, selecting another playlist in the original track "forgets" the playlist for this track.You can't select the shared playlist anymore from the original track. This commit adds a way to keep trace of shared playlist between tracks.
558 lines
13 KiB
C++
558 lines
13 KiB
C++
/*
|
|
Copyright (C) 2009 Paul Davis
|
|
|
|
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 <vector>
|
|
|
|
#include "ardour/debug.h"
|
|
#include "ardour/playlist.h"
|
|
#include "ardour/playlist_factory.h"
|
|
#include "ardour/session_playlists.h"
|
|
#include "ardour/track.h"
|
|
#include "pbd/i18n.h"
|
|
#include "pbd/compose.h"
|
|
#include "pbd/xml++.h"
|
|
|
|
using namespace std;
|
|
using namespace PBD;
|
|
using namespace ARDOUR;
|
|
|
|
SessionPlaylists::~SessionPlaylists ()
|
|
{
|
|
DEBUG_TRACE (DEBUG::Destruction, "delete playlists\n");
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ) {
|
|
SessionPlaylists::List::iterator tmp;
|
|
|
|
tmp = i;
|
|
++tmp;
|
|
|
|
DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for used playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
|
|
boost::shared_ptr<Playlist> keeper (*i);
|
|
(*i)->drop_references ();
|
|
|
|
i = tmp;
|
|
}
|
|
|
|
DEBUG_TRACE (DEBUG::Destruction, "delete unused playlists\n");
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) {
|
|
List::iterator tmp;
|
|
|
|
tmp = i;
|
|
++tmp;
|
|
|
|
DEBUG_TRACE(DEBUG::Destruction, string_compose ("Dropping for unused playlist %1 ; pre-ref = %2\n", (*i)->name(), (*i).use_count()));
|
|
boost::shared_ptr<Playlist> keeper (*i);
|
|
(*i)->drop_references ();
|
|
|
|
i = tmp;
|
|
}
|
|
|
|
playlists.clear ();
|
|
unused_playlists.clear ();
|
|
}
|
|
|
|
bool
|
|
SessionPlaylists::add (boost::shared_ptr<Playlist> playlist)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
bool const existing = find (playlists.begin(), playlists.end(), playlist) != playlists.end();
|
|
|
|
if (!existing) {
|
|
playlists.insert (playlists.begin(), playlist);
|
|
playlist->InUse.connect_same_thread (*this, boost::bind (&SessionPlaylists::track, this, _1, boost::weak_ptr<Playlist>(playlist)));
|
|
playlist->DropReferences.connect_same_thread (
|
|
*this, boost::bind (&SessionPlaylists::remove_weak, this, boost::weak_ptr<Playlist> (playlist))
|
|
);
|
|
}
|
|
|
|
return existing;
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::remove_weak (boost::weak_ptr<Playlist> playlist)
|
|
{
|
|
boost::shared_ptr<Playlist> p = playlist.lock ();
|
|
if (p) {
|
|
remove (p);
|
|
}
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::remove (boost::shared_ptr<Playlist> playlist)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
List::iterator i;
|
|
|
|
i = find (playlists.begin(), playlists.end(), playlist);
|
|
if (i != playlists.end()) {
|
|
playlists.erase (i);
|
|
}
|
|
|
|
i = find (unused_playlists.begin(), unused_playlists.end(), playlist);
|
|
if (i != unused_playlists.end()) {
|
|
unused_playlists.erase (i);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
SessionPlaylists::track (bool inuse, boost::weak_ptr<Playlist> wpl)
|
|
{
|
|
boost::shared_ptr<Playlist> pl(wpl.lock());
|
|
|
|
if (!pl) {
|
|
return;
|
|
}
|
|
|
|
List::iterator x;
|
|
|
|
if (pl->hidden()) {
|
|
/* its not supposed to be visible */
|
|
return;
|
|
}
|
|
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
if (!inuse) {
|
|
|
|
unused_playlists.insert (pl);
|
|
|
|
if ((x = playlists.find (pl)) != playlists.end()) {
|
|
playlists.erase (x);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
playlists.insert (pl);
|
|
|
|
if ((x = unused_playlists.find (pl)) != unused_playlists.end()) {
|
|
unused_playlists.erase (x);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t
|
|
SessionPlaylists::n_playlists () const
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
return playlists.size();
|
|
}
|
|
|
|
boost::shared_ptr<Playlist>
|
|
SessionPlaylists::by_name (string name)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
if ((*i)->name() == name) {
|
|
return* i;
|
|
}
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
if ((*i)->name() == name) {
|
|
return* i;
|
|
}
|
|
}
|
|
|
|
return boost::shared_ptr<Playlist>();
|
|
}
|
|
|
|
boost::shared_ptr<Playlist>
|
|
SessionPlaylists::by_id (const PBD::ID& id)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
if ((*i)->id() == id) {
|
|
return* i;
|
|
}
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
if ((*i)->id() == id) {
|
|
return* i;
|
|
}
|
|
}
|
|
|
|
return boost::shared_ptr<Playlist>();
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::unassigned (std::list<boost::shared_ptr<Playlist> > & list)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
|
|
list.push_back (*i);
|
|
}
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
if (!(*i)->get_orig_track_id().to_s().compare ("0")) {
|
|
list.push_back (*i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::get (vector<boost::shared_ptr<Playlist> >& s) const
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
s.push_back (*i);
|
|
}
|
|
|
|
for (List::const_iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
s.push_back (*i);
|
|
}
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::destroy_region (boost::shared_ptr<Region> r)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
(*i)->destroy_region (r);
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
(*i)->destroy_region (r);
|
|
}
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
|
|
{
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i)
|
|
(*i)->get_region_list_equivalent_regions (region, result);
|
|
}
|
|
|
|
/** Return the number of playlists (not regions) that contain @a src
|
|
* Important: this counts usage in both used and not-used playlists.
|
|
*/
|
|
uint32_t
|
|
SessionPlaylists::source_use_count (boost::shared_ptr<const Source> src) const
|
|
{
|
|
uint32_t count = 0;
|
|
|
|
/* XXXX this can go wildly wrong in the presence of circular references
|
|
* between compound regions.
|
|
*/
|
|
|
|
for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
|
|
if ((*p)->uses_source (src)) {
|
|
++count;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (List::const_iterator p = unused_playlists.begin(); p != unused_playlists.end(); ++p) {
|
|
if ((*p)->uses_source (src)) {
|
|
++count;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::sync_all_regions_with_regions ()
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
for (List::const_iterator p = playlists.begin(); p != playlists.end(); ++p) {
|
|
(*p)->sync_all_regions_with_regions ();
|
|
}
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::update_after_tempo_map_change ()
|
|
{
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
(*i)->update_after_tempo_map_change ();
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
(*i)->update_after_tempo_map_change ();
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
struct id_compare
|
|
{
|
|
bool operator()(const boost::shared_ptr<Playlist>& p1, const boost::shared_ptr<Playlist>& p2)
|
|
{
|
|
return p1->id () < p2->id ();
|
|
}
|
|
};
|
|
|
|
typedef std::set<boost::shared_ptr<Playlist> > List;
|
|
typedef std::set<boost::shared_ptr<Playlist>, id_compare> IDSortedList;
|
|
|
|
static void
|
|
get_id_sorted_playlists (const List& playlists, IDSortedList& id_sorted_playlists)
|
|
{
|
|
for (List::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
id_sorted_playlists.insert(*i);
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
void
|
|
SessionPlaylists::add_state (XMLNode* node, bool full_state)
|
|
{
|
|
XMLNode* child = node->add_child ("Playlists");
|
|
|
|
IDSortedList id_sorted_playlists;
|
|
get_id_sorted_playlists (playlists, id_sorted_playlists);
|
|
|
|
for (IDSortedList::iterator i = id_sorted_playlists.begin (); i != id_sorted_playlists.end (); ++i) {
|
|
if (!(*i)->hidden ()) {
|
|
if (full_state) {
|
|
child->add_child_nocopy ((*i)->get_state ());
|
|
} else {
|
|
child->add_child_nocopy ((*i)->get_template ());
|
|
}
|
|
}
|
|
}
|
|
|
|
child = node->add_child ("UnusedPlaylists");
|
|
|
|
IDSortedList id_sorted_unused_playlists;
|
|
get_id_sorted_playlists (unused_playlists, id_sorted_unused_playlists);
|
|
|
|
for (IDSortedList::iterator i = id_sorted_unused_playlists.begin ();
|
|
i != id_sorted_unused_playlists.end (); ++i) {
|
|
if (!(*i)->hidden()) {
|
|
if (!(*i)->empty()) {
|
|
if (full_state) {
|
|
child->add_child_nocopy ((*i)->get_state());
|
|
} else {
|
|
child->add_child_nocopy ((*i)->get_template());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @return true for `stop cleanup', otherwise false */
|
|
bool
|
|
SessionPlaylists::maybe_delete_unused (boost::function<int(boost::shared_ptr<Playlist>)> ask)
|
|
{
|
|
vector<boost::shared_ptr<Playlist> > playlists_tbd;
|
|
|
|
bool delete_remaining = false;
|
|
bool keep_remaining = false;
|
|
|
|
for (List::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
|
|
|
|
if (keep_remaining) {
|
|
break;
|
|
}
|
|
|
|
if (delete_remaining) {
|
|
playlists_tbd.push_back (*x);
|
|
continue;
|
|
}
|
|
|
|
int status = ask (*x);
|
|
|
|
switch (status) {
|
|
case -1:
|
|
// abort
|
|
return true;
|
|
|
|
case -2:
|
|
// keep this and all later
|
|
keep_remaining = true;
|
|
break;
|
|
|
|
case 2:
|
|
// delete this and all later
|
|
delete_remaining = true;
|
|
// no break;
|
|
|
|
case 1:
|
|
// delete this
|
|
playlists_tbd.push_back (*x);
|
|
break;
|
|
|
|
default:
|
|
/* leave it alone */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* now delete any that were marked for deletion */
|
|
|
|
for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
|
|
boost::shared_ptr<Playlist> keeper (*x);
|
|
(*x)->drop_references ();
|
|
}
|
|
|
|
playlists_tbd.clear ();
|
|
|
|
return false;
|
|
}
|
|
|
|
int
|
|
SessionPlaylists::load (Session& session, const XMLNode& node)
|
|
{
|
|
XMLNodeList nlist;
|
|
XMLNodeConstIterator niter;
|
|
boost::shared_ptr<Playlist> playlist;
|
|
|
|
nlist = node.children();
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
|
|
error << _("Session: cannot create Playlist from XML description.") << endmsg;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SessionPlaylists::load_unused (Session& session, const XMLNode& node)
|
|
{
|
|
XMLNodeList nlist;
|
|
XMLNodeConstIterator niter;
|
|
boost::shared_ptr<Playlist> playlist;
|
|
|
|
nlist = node.children();
|
|
|
|
for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
|
|
|
|
if ((playlist = XMLPlaylistFactory (session, **niter)) == 0) {
|
|
error << _("Session: cannot create Playlist from XML description.") << endmsg;
|
|
continue;
|
|
}
|
|
|
|
// now manually untrack it
|
|
|
|
track (false, boost::weak_ptr<Playlist> (playlist));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
boost::shared_ptr<Playlist>
|
|
SessionPlaylists::XMLPlaylistFactory (Session& session, const XMLNode& node)
|
|
{
|
|
try {
|
|
return PlaylistFactory::create (session, node);
|
|
}
|
|
|
|
catch (failed_constructor& err) {
|
|
return boost::shared_ptr<Playlist>();
|
|
}
|
|
}
|
|
|
|
boost::shared_ptr<Crossfade>
|
|
SessionPlaylists::find_crossfade (const PBD::ID& id)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
|
|
boost::shared_ptr<Crossfade> c;
|
|
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
c = (*i)->find_crossfade (id);
|
|
if (c) {
|
|
return c;
|
|
}
|
|
}
|
|
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
c = (*i)->find_crossfade (id);
|
|
if (c) {
|
|
return c;
|
|
}
|
|
}
|
|
|
|
return boost::shared_ptr<Crossfade> ();
|
|
}
|
|
|
|
uint32_t
|
|
SessionPlaylists::region_use_count (boost::shared_ptr<Region> region) const
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
uint32_t cnt = 0;
|
|
|
|
for (List::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
|
cnt += (*i)->region_use_count (region);
|
|
}
|
|
|
|
for (List::const_iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
|
|
cnt += (*i)->region_use_count (region);
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/** @return list of Playlists that are associated with a track */
|
|
vector<boost::shared_ptr<Playlist> >
|
|
SessionPlaylists::playlists_for_track (boost::shared_ptr<Track> tr) const
|
|
{
|
|
vector<boost::shared_ptr<Playlist> > pl;
|
|
get (pl);
|
|
|
|
vector<boost::shared_ptr<Playlist> > pl_tr;
|
|
|
|
for (vector<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
|
|
if ( ((*i)->get_orig_track_id() == tr->id()) ||
|
|
(tr->playlist()->id() == (*i)->id()) ||
|
|
((*i)->shared_with (tr->id())) )
|
|
{
|
|
pl_tr.push_back (*i);
|
|
}
|
|
}
|
|
|
|
return pl_tr;
|
|
}
|
|
|
|
void
|
|
SessionPlaylists::foreach (boost::function<void(boost::shared_ptr<const Playlist>)> functor, bool incl_unused)
|
|
{
|
|
Glib::Threads::Mutex::Lock lm (lock);
|
|
for (List::iterator i = playlists.begin(); i != playlists.end(); i++) {
|
|
if (!(*i)->hidden()) {
|
|
functor (*i);
|
|
}
|
|
}
|
|
if (!incl_unused) {
|
|
return;
|
|
}
|
|
for (List::iterator i = unused_playlists.begin(); i != unused_playlists.end(); i++) {
|
|
if (!(*i)->hidden()) {
|
|
functor (*i);
|
|
}
|
|
}
|
|
}
|