X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Faudio_playlist.cc;h=5019935d5d0802587dae7b4e91c077f2b06855d9;hb=3150041423a199189c9b6ab292a078c51d9670c2;hp=cce6e188f8b3bb8a484ba93f68fe314485cb605e;hpb=fbb9576d4047c03276cc2e1b750465c3b0371c6c;p=ardour.git diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index cce6e188f8..5019935d5d 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2003 Paul Davis + Copyright (C) 2003 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 @@ -15,80 +15,143 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include #include -#include - -#include -#include -#include -#include -#include -#include -#include +#include "ardour/types.h" +#include "ardour/debug.h" +#include "ardour/configuration.h" +#include "ardour/audioplaylist.h" +#include "ardour/audioregion.h" +#include "ardour/crossfade.h" +#include "ardour/crossfade_compare.h" +#include "ardour/session.h" +#include "pbd/enumwriter.h" #include "i18n.h" using namespace ARDOUR; -using namespace sigc; using namespace std; using namespace PBD; +namespace ARDOUR { + namespace Properties { + PBD::PropertyDescriptor crossfades; + } +} + +void +AudioPlaylist::make_property_quarks () +{ + Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades")); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id)); +} + +CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl) + : SequenceProperty > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1)) + , _playlist (pl) +{ + +} + +CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p) + : PBD::SequenceProperty > > (p) + , _playlist (p._playlist) +{ + +} + + +CrossfadeListProperty * +CrossfadeListProperty::create () const +{ + return new CrossfadeListProperty (_playlist); +} + +CrossfadeListProperty * +CrossfadeListProperty::clone () const +{ + return new CrossfadeListProperty (*this); +} + +void +CrossfadeListProperty::get_content_as_xml (boost::shared_ptr xfade, XMLNode & node) const +{ + /* Crossfades are not written to any state when they are no + longer in use, so we must write their state here. + */ + + XMLNode& c = xfade->get_state (); + node.add_child_nocopy (c); +} + +boost::shared_ptr +CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const +{ + XMLNodeList const c = node.children (); + assert (c.size() == 1); + return boost::shared_ptr (new Crossfade (_playlist, *c.front())); +} + + AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden) - : Playlist (session, node, hidden) + : Playlist (session, node, DataType::AUDIO, hidden) + , _crossfades (*this) { +#ifndef NDEBUG + const XMLProperty* prop = node.property("type"); + assert(!prop || DataType(prop->value()) == DataType::AUDIO); +#endif + + add_property (_crossfades); + in_set_state++; - set_state (node); + set_state (node, Stateful::loading_state_version); in_set_state--; - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) - : Playlist (session, name, hidden) + : Playlist (session, name, DataType::AUDIO, hidden) + , _crossfades (*this) { - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } - + add_property (_crossfades); } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr other, string name, bool hidden) : Playlist (other, name, hidden) + , _crossfades (*this) { - RegionList::const_iterator in_o = other.regions.begin(); + add_property (_crossfades); + + RegionList::const_iterator in_o = other->regions.begin(); RegionList::iterator in_n = regions.begin(); - while (in_o != other.regions.end()) { + while (in_o != other->regions.end()) { boost::shared_ptr ar = boost::dynamic_pointer_cast(*in_o); // We look only for crossfades which begin with the current region, so we don't get doubles - for (list::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) { + for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) { if ((*xfades)->in() == ar) { // We found one! Now copy it! - RegionList::const_iterator out_o = other.regions.begin(); + RegionList::const_iterator out_o = other->regions.begin(); RegionList::const_iterator out_n = regions.begin(); - while (out_o != other.regions.end()) { - + while (out_o != other->regions.end()) { + boost::shared_ptrar2 = boost::dynamic_pointer_cast(*out_o); - + if ((*xfades)->out() == ar2) { boost::shared_ptrin = boost::dynamic_pointer_cast(*in_n); boost::shared_ptrout = boost::dynamic_pointer_cast(*out_n); - Crossfade *new_fade = new Crossfade (*(*xfades), in, out); - add_crossfade(*new_fade); + boost::shared_ptr new_fade = boost::shared_ptr (new Crossfade (*xfades, in, out)); + add_crossfade(new_fade); break; } - + out_o++; out_n++; } @@ -99,54 +162,29 @@ AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, string name, bool hidd in_o++; in_n++; } - - if (!hidden) { - PlaylistCreated (this); /* EMIT SIGNAL */ - } } -AudioPlaylist::AudioPlaylist (const AudioPlaylist& other, nframes_t start, nframes_t cnt, string name, bool hidden) +AudioPlaylist::AudioPlaylist (boost::shared_ptr other, nframes_t start, nframes_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) + , _crossfades (*this) { + add_property (_crossfades); + /* this constructor does NOT notify others (session) */ } AudioPlaylist::~AudioPlaylist () { - set all_xfades; - - GoingAway (); /* EMIT SIGNAL */ - - /* drop connections to signals */ - - notify_callbacks (); - - - cerr << "deleting crossfades " << _crossfades.size() << endl; - - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end(); ) { - Crossfades::iterator tmp; - - tmp = x; - ++tmp; - - delete *x; - - cerr << _crossfades.size() << " to go\n"; - - x = tmp; - } - - cerr << "done\n"; + _crossfades.clear (); } struct RegionSortByLayer { - bool operator() (boost::shared_ptra, boost::shared_ptrb) { + bool operator() (boost::shared_ptr a, boost::shared_ptr b) { return a->layer() < b->layer(); } }; -nframes_t +ARDOUR::nframes_t AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start, nframes_t cnt, unsigned chan_n) { @@ -156,9 +194,9 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf nframes_t skip_frames; /* optimizing this memset() away involves a lot of conditionals - that may well cause more of a hit due to cache misses + that may well cause more of a hit due to cache misses and related stuff than just doing this here. - + it would be great if someone could measure this at some point. @@ -170,27 +208,35 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf memset (buf, 0, sizeof (Sample) * cnt); - /* this function is never called from a realtime thread, so + /* this function is never called from a realtime thread, so its OK to block (for short intervals). */ - Glib::Mutex::Lock rm (region_lock); + Glib::RecMutex::Lock rm (region_lock); end = start + cnt - 1; - read_frames = 0; skip_frames = 0; _read_data_count = 0; + _read_data_count = 0; + + RegionList* rlist = regions_to_read (start, start+cnt); + + if (rlist->empty()) { + delete rlist; + return cnt; + } + map > > relevant_regions; - map > relevant_xfades; + map > > relevant_xfades; vector relevant_layers; - for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { + for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) { if ((*i)->coverage (start, end) != OverlapNone) { relevant_regions[(*i)->layer()].push_back (*i); relevant_layers.push_back ((*i)->layer()); - } + } } for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { @@ -213,16 +259,18 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { vector > r (relevant_regions[*l]); - vector& x (relevant_xfades[*l]); + vector >& x (relevant_xfades[*l]); + for (vector >::iterator i = r.begin(); i != r.end(); ++i) { boost::shared_ptr ar = boost::dynamic_pointer_cast(*i); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name())); assert(ar); ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames); _read_data_count += ar->read_data_count(); } - - for (vector::iterator i = x.begin(); i != x.end(); ++i) { + + for (vector >::iterator i = x.begin(); i != x.end(); ++i) { (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); /* don't JACK up _read_data_count, since its the same data as we just @@ -231,6 +279,7 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf } } + delete rlist; return ret; } @@ -238,37 +287,33 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nf void AudioPlaylist::remove_dependents (boost::shared_ptr region) { - Crossfades::iterator i, tmp; boost::shared_ptr r = boost::dynamic_pointer_cast (region); if (in_set_state) { return; } - + if (r == 0) { fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") << endmsg; return; } - for (i = _crossfades.begin(); i != _crossfades.end(); ) { - tmp = i; - tmp++; + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { - if ((*i)->involves (r)) { - delete *i; + i = _crossfades.erase (i); + } else { + ++i; } - - i = tmp; } } void -AudioPlaylist::flush_notifications () +AudioPlaylist::flush_notifications (bool from_undo) { - Playlist::flush_notifications(); + Playlist::flush_notifications (from_undo); if (in_flush) { return; @@ -282,7 +327,7 @@ AudioPlaylist::flush_notifications () } _pending_xfade_adds.clear (); - + in_flush = false; } @@ -290,7 +335,7 @@ void AudioPlaylist::refresh_dependents (boost::shared_ptr r) { boost::shared_ptr ar = boost::dynamic_pointer_cast(r); - set updated; + set > updated; if (ar == 0) { return; @@ -299,7 +344,7 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr r) for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { Crossfades::iterator tmp; - + tmp = x; ++tmp; @@ -307,10 +352,16 @@ AudioPlaylist::refresh_dependents (boost::shared_ptr r) if ((*x)->involves (ar)) { - if (find (updated.begin(), updated.end(), *x) == updated.end()) { - if ((*x)->refresh ()) { - /* not invalidated by the refresh */ - updated.insert (*x); + pair >::iterator, bool> const u = updated.insert (*x); + + if (u.second) { + /* x was successfully inserted into the set, so it has not already been updated */ + try { + (*x)->refresh (); + } + + catch (Crossfade::NoCrossfadeHere& err) { + // relax, Invalidated during refresh } } } @@ -331,29 +382,29 @@ AudioPlaylist::finalize_split_region (boost::shared_ptr o, boost::shared tmp = x; ++tmp; - Crossfade *fade = 0; - + boost::shared_ptr fade; + if ((*x)->_in == orig) { if (! (*x)->covers(right->position())) { - fade = new Crossfade (**x, left, (*x)->_out); + fade = boost::shared_ptr (new Crossfade (*x, left, (*x)->_out)); } else { // Overlap, the crossfade is copied on the left side of the right region instead - fade = new Crossfade (**x, right, (*x)->_out); + fade = boost::shared_ptr (new Crossfade (*x, right, (*x)->_out)); } } - + if ((*x)->_out == orig) { if (! (*x)->covers(right->position())) { - fade = new Crossfade (**x, (*x)->_in, right); + fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, right)); } else { // Overlap, the crossfade is copied on the right side of the left region instead - fade = new Crossfade (**x, (*x)->_in, left); + fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, left)); } } - + if (fade) { _crossfades.remove (*x); - add_crossfade (*fade); + add_crossfade (fade); } x = tmp; } @@ -366,14 +417,13 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) boost::shared_ptr region; boost::shared_ptr top; boost::shared_ptr bottom; - Crossfade* xfade; + boost::shared_ptr xfade; + RegionList* touched_regions = 0; if (in_set_state || in_partition) { return; } - cerr << "Check dependents of " << r->name() << endl; - if ((region = boost::dynamic_pointer_cast (r)) == 0) { fatal << _("programming error: non-audio Region tested for overlap in audio playlist") << endmsg; @@ -384,12 +434,12 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) refresh_dependents (r); } - if (!Config->get_auto_xfade()) { + + if (!_session.config.get_auto_xfade()) { return; } for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) { - other = boost::dynamic_pointer_cast (*i); if (other == region) { @@ -400,6 +450,7 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) continue; } + if (other->layer() < region->layer()) { top = region; bottom = other; @@ -408,81 +459,152 @@ AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) bottom = region; } + if (!top->opaque()) { + continue; + } + + OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); + + delete touched_regions; + touched_regions = 0; + try { - - if (top->coverage (bottom->position(), bottom->last_frame()) != OverlapNone) { - - /* check if the upper region is within the lower region */ - - if (top->first_frame() > bottom->first_frame() && - top->last_frame() < bottom->last_frame()) { - - - /* [ -------- top ------- ] - * {=========== bottom =============} - */ - - /* to avoid discontinuities at the region boundaries of an internal - overlap (this region is completely within another), we create - two hidden crossfades at each boundary. this is not dependent - on the auto-xfade option, because we require it as basic - audio engineering. + framecnt_t xfade_length; + switch (c) { + case OverlapNone: + break; + + case OverlapInternal: + /* {=============== top =============} + * [ ----- bottom ------- ] + */ + break; + + case OverlapExternal: + + /* [ -------- top ------- ] + * {=========== bottom =============} + */ + + /* to avoid discontinuities at the region boundaries of an internal + overlap (this region is completely within another), we create + two hidden crossfades at each boundary. this is not dependent + on the auto-xfade option, because we require it as basic + audio engineering. + */ + + xfade_length = min ((framecnt_t) 720, top->length()); + + if (top_region_at (top->first_frame()) == top) { + + xfade = boost::shared_ptr (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn)); + add_crossfade (xfade); + } + + if (top_region_at (top->last_frame() - 1) == top) { + + /* + only add a fade out if there is no region on top of the end of 'top' (which + would cover it). */ - - nframes_t xfade_length = min ((nframes_t) 720, top->length()); - - /* in, out */ - xfade = new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn); - add_crossfade (*xfade); - xfade = new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut); - add_crossfade (*xfade); - + + xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut)); + add_crossfade (xfade); + } + break; + case OverlapStart: + + /* { ==== top ============ } + * [---- bottom -------------------] + */ + + if (_session.config.get_xfade_model() == FullCrossfade) { + touched_regions = regions_touched (top->first_frame(), bottom->last_frame()); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } else { - xfade = new Crossfade (other, region, Config->get_xfade_model(), Config->get_xfades_active()); - add_crossfade (*xfade); + touched_regions = regions_touched (top->first_frame(), + top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(), + top->length())); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } + } + break; + case OverlapEnd: + + + /* [---- top ------------------------] + * { ==== bottom ============ } + */ + + if (_session.config.get_xfade_model() == FullCrossfade) { + + touched_regions = regions_touched (bottom->first_frame(), top->last_frame()); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, + _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } + + } else { + touched_regions = regions_touched (bottom->first_frame(), + bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(), + bottom->length())); + if (touched_regions->size() <= 2) { + xfade = boost::shared_ptr (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } - } + break; + default: + xfade = boost::shared_ptr (new Crossfade (region, other, + _session.config.get_xfade_model(), _session.config.get_xfades_active())); + add_crossfade (xfade); + } } - + catch (failed_constructor& err) { continue; } - + catch (Crossfade::NoCrossfadeHere& err) { continue; } - + } + + delete touched_regions; } void -AudioPlaylist::add_crossfade (Crossfade& xfade) +AudioPlaylist::add_crossfade (boost::shared_ptr xfade) { Crossfades::iterator ci; - cerr << "adding xfade involving " << xfade.in()->name() << " and " << xfade.out()->name() << endl; - for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { - cerr << "\tcompare to " << (*ci)->in()->name() << " and " << (*ci)->out()->name() << endl; - if (*(*ci) == xfade) { // Crossfade::operator==() + if (*(*ci) == *xfade) { // Crossfade::operator==() break; } } - + if (ci != _crossfades.end()) { - delete &xfade; + // it will just go away } else { - _crossfades.push_back (&xfade); + _crossfades.push_back (xfade); - xfade.Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - xfade.StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed)); + xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); + xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); - notify_crossfade_added (&xfade); + notify_crossfade_added (xfade); } } - -void AudioPlaylist::notify_crossfade_added (Crossfade *x) + +void AudioPlaylist::notify_crossfade_added (boost::shared_ptr x) { if (g_atomic_int_get(&block_notifications)) { _pending_xfade_adds.insert (_pending_xfade_adds.end(), x); @@ -492,9 +614,10 @@ void AudioPlaylist::notify_crossfade_added (Crossfade *x) } void -AudioPlaylist::crossfade_invalidated (Crossfade* xfade) +AudioPlaylist::crossfade_invalidated (boost::shared_ptr r) { Crossfades::iterator i; + boost::shared_ptr xfade = boost::dynamic_pointer_cast (r); xfade->in()->resume_fade_in (); xfade->out()->resume_fade_out (); @@ -505,16 +628,17 @@ AudioPlaylist::crossfade_invalidated (Crossfade* xfade) } int -AudioPlaylist::set_state (const XMLNode& node) +AudioPlaylist::set_state (const XMLNode& node, int version) { XMLNode *child; XMLNodeList nlist; XMLNodeConstIterator niter; in_set_state++; - freeze (); - Playlist::set_state (node); + Playlist::set_state (node, version); + + freeze (); nlist = node.children(); @@ -527,23 +651,23 @@ AudioPlaylist::set_state (const XMLNode& node) } try { - Crossfade* xfade = new Crossfade (*((const Playlist *)this), *child); + boost::shared_ptr xfade = boost::shared_ptr (new Crossfade (*((const Playlist *)this), *child)); _crossfades.push_back (xfade); - xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated)); - xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed)); + xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1)); + xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1)); NewCrossfade(xfade); } - + catch (failed_constructor& err) { // cout << string_compose (_("could not create crossfade object in playlist %1"), - // _name) + // _name) // << endl; continue; } } thaw (); - in_set_state++; + in_set_state--; return 0; } @@ -551,19 +675,7 @@ AudioPlaylist::set_state (const XMLNode& node) void AudioPlaylist::clear (bool with_signals) { - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { - - Crossfades::iterator tmp; - tmp = i; - ++tmp; - - delete *i; - - i = tmp; - } - _crossfades.clear (); - Playlist::clear (with_signals); } @@ -577,7 +689,7 @@ AudioPlaylist::state (bool full_state) node.add_child_nocopy ((*i)->get_state()); } } - + return node; } @@ -585,7 +697,7 @@ void AudioPlaylist::dump () const { boost::shared_ptrr; - Crossfade *x; + boost::shared_ptr x; cerr << "Playlist \"" << _name << "\" " << endl << regions.size() << " regions " @@ -594,9 +706,9 @@ AudioPlaylist::dump () const for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { r = *i; - cerr << " " << r->name() << " @ " << r << " [" - << r->start() << "+" << r->length() - << "] at " + cerr << " " << r->name() << " @ " << r << " [" + << r->start() << "+" << r->length() + << "] at " << r->position() << " on layer " << r->layer () @@ -605,13 +717,13 @@ AudioPlaylist::dump () const for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { x = *i; - cerr << " xfade [" + cerr << " xfade [" << x->out()->name() << ',' << x->in()->name() << " @ " << x->position() - << " length = " + << " length = " << x->length () << " active ? " << (x->active() ? "yes" : "no") @@ -623,34 +735,45 @@ bool AudioPlaylist::destroy_region (boost::shared_ptr region) { boost::shared_ptr r = boost::dynamic_pointer_cast (region); + + if (!r) { + return false; + } + bool changed = false; Crossfades::iterator c, ctmp; - set unique_xfades; + set > unique_xfades; - if (r == 0) { - fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") - << endmsg; - /*NOTREACHED*/ - return false; - } - - { + { RegionLock rlock (this); - RegionList::iterator i; - RegionList::iterator tmp; - for (i = regions.begin(); i != regions.end(); ) { - - tmp = i; + for (RegionList::iterator i = regions.begin(); i != regions.end(); ) { + + RegionList::iterator tmp = i; ++tmp; - + if ((*i) == region) { regions.erase (i); changed = true; } - + i = tmp; } + + for (set >::iterator x = all_regions.begin(); x != all_regions.end(); ) { + + set >::iterator xtmp = x; + ++xtmp; + + if ((*x) == region) { + all_regions.erase (x); + changed = true; + } + + x = xtmp; + } + + region->set_playlist (boost::shared_ptr()); } for (c = _crossfades.begin(); c != _crossfades.end(); ) { @@ -661,12 +784,8 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) unique_xfades.insert (*c); _crossfades.erase (c); } - - c = ctmp; - } - for (set::iterator c = unique_xfades.begin(); c != unique_xfades.end(); ++c) { - delete *c; + c = ctmp; } if (changed) { @@ -678,7 +797,7 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) } void -AudioPlaylist::crossfade_changed (Change ignored) +AudioPlaylist::crossfade_changed (const PropertyChange&) { if (in_flush || in_set_state) { return; @@ -690,32 +809,35 @@ AudioPlaylist::crossfade_changed (Change ignored) that occured. */ - notify_modified (); + notify_contents_changed (); } bool -AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr region) +AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) { if (in_flush || in_set_state) { return false; } - Change our_interests = Change (AudioRegion::FadeInChanged| - AudioRegion::FadeOutChanged| - AudioRegion::FadeInActiveChanged| - AudioRegion::FadeOutActiveChanged| - AudioRegion::EnvelopeActiveChanged| - AudioRegion::ScaleAmplitudeChanged| - AudioRegion::EnvelopeChanged); + PropertyChange our_interests; + + our_interests.add (Properties::fade_in_active); + our_interests.add (Properties::fade_out_active); + our_interests.add (Properties::scale_amplitude); + our_interests.add (Properties::envelope_active); + our_interests.add (Properties::envelope); + our_interests.add (Properties::fade_in); + our_interests.add (Properties::fade_out); + bool parent_wants_notify; parent_wants_notify = Playlist::region_changed (what_changed, region); - if ((parent_wants_notify || (what_changed & our_interests))) { - notify_modified (); + if (parent_wants_notify || (what_changed.contains (our_interests))) { + notify_contents_changed (); } - return true; + return true; } void @@ -731,7 +853,25 @@ AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist) if (frame >= start && frame <= end) { clist.push_back (*i); - } + } } } +void +AudioPlaylist::foreach_crossfade (boost::function)> s) +{ + RegionLock rl (this, false); + for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { + s (*i); + } +} + +void +AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change) +{ + for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) { + add_crossfade (*i); + } + + /* don't remove crossfades here; they will be dealt with by the dependency code */ +}