From a2897ecef6da6a458aa1de8c2d9973a1e809dca2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 16 Apr 2012 16:32:22 +0000 Subject: Fairly major change to the way in which crossfades are handled; they are now done with region fades, rather than separate objects. After this commit, Ardour will try to convert your session files to the new crossfade format, but will make a backup in your session folder first. If you have works in progress using Ardour 3 it is ***STRONGLY RECOMMENDED*** that you back up session files before updating to this commit. git-svn-id: svn://localhost/ardour2/branches/3.0@11986 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/audio_playlist.cc | 945 ++++++++---------------------------------- 1 file changed, 174 insertions(+), 771 deletions(-) (limited to 'libs/ardour/audio_playlist.cc') diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index 76d0228547..bcb0219e2a 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -26,7 +26,6 @@ #include "ardour/configuration.h" #include "ardour/audioplaylist.h" #include "ardour/audioregion.h" -#include "ardour/crossfade.h" #include "ardour/region_sorters.h" #include "ardour/session.h" #include "pbd/enumwriter.h" @@ -37,77 +36,14 @@ using namespace ARDOUR; 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, 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++; if (set_state (node, Stateful::loading_state_version)) { throw failed_constructor(); @@ -119,64 +55,20 @@ AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden) : Playlist (session, name, DataType::AUDIO, hidden) - , _crossfades (*this) { - add_property (_crossfades); } AudioPlaylist::AudioPlaylist (boost::shared_ptr other, string name, bool hidden) : Playlist (other, name, hidden) - , _crossfades (*this) { - add_property (_crossfades); - - RegionList::const_iterator in_o = other->regions.begin(); - RegionList::iterator in_n = regions.begin(); - - 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 (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_n = regions.begin(); - - 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); - boost::shared_ptr new_fade = boost::shared_ptr (new Crossfade (*xfades, in, out)); - add_crossfade(new_fade); - break; - } - - out_o++; - out_n++; - } -// cerr << "HUH!? second region in the crossfade not found!" << endl; - } - } - - in_o++; - in_n++; - } } AudioPlaylist::AudioPlaylist (boost::shared_ptr other, framepos_t start, framecnt_t cnt, string name, bool hidden) : Playlist (other, start, cnt, name, hidden) - , _crossfades (*this) { RegionLock rlock2 (const_cast (other.get())); in_set_state++; - add_property (_crossfades); - framepos_t const end = start + cnt - 1; /* Audio regions that have been created by the Playlist constructor @@ -194,10 +86,10 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram framecnt_t fade_out = 64; switch (region->coverage (start, end)) { - case OverlapNone: + case Evoral::OverlapNone: continue; - case OverlapInternal: + case Evoral::OverlapInternal: { framecnt_t const offset = start - region->position (); framecnt_t const trim = region->last_frame() - end; @@ -210,7 +102,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapStart: { + case Evoral::OverlapStart: { if (end > region->position() + region->fade_in()->back()->when) fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in if (end > region->last_frame() - region->fade_out()->back()->when) @@ -218,7 +110,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapEnd: { + case Evoral::OverlapEnd: { if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout fade_out = region->fade_out()->back()->when; @@ -227,7 +119,7 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram break; } - case OverlapExternal: + case Evoral::OverlapExternal: fade_in = region->fade_in()->back()->when; fade_out = region->fade_out()->back()->when; break; @@ -246,14 +138,14 @@ AudioPlaylist::AudioPlaylist (boost::shared_ptr other, fram /* this constructor does NOT notify others (session) */ } -AudioPlaylist::~AudioPlaylist () -{ - _crossfades.clear (); -} - -struct RegionSortByLayer { +/** Sort by descending layer and then by ascending position */ +struct ReadSorter { bool operator() (boost::shared_ptr a, boost::shared_ptr b) { - return a->layer() < b->layer(); + if (a->layer() != b->layer()) { + return a->layer() > b->layer(); + } + + return a->position() < b->position(); } }; @@ -261,10 +153,8 @@ ARDOUR::framecnt_t AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start, framecnt_t cnt, unsigned chan_n) { - framecnt_t ret = cnt; - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n", - name(), start, cnt, chan_n, regions.size(), _crossfades.size())); + DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5\n", + name(), start, cnt, chan_n, regions.size())); /* optimizing this memset() away involves a lot of conditionals that may well cause more of a hit due to cache misses @@ -287,498 +177,178 @@ AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, fr Glib::RecMutex::Lock rm (region_lock); - framepos_t const end = start + cnt - 1; - - boost::shared_ptr rlist = regions_to_read (start, start+cnt); - - if (rlist->empty()) { - return cnt; - } - - map > > relevant_regions; - map > > relevant_xfades; - vector relevant_layers; - - 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()); - } - } - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size())); - - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n", - name(), (*i)->out()->name(), (*i)->in()->name(), - (*i)->first_frame(), (*i)->last_frame(), - start, end)); - if ((*i)->coverage (start, end) != OverlapNone) { - relevant_xfades[(*i)->upper_layer()].push_back (*i); - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n", - (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end)))); - } - } - -// RegionSortByLayer layer_cmp; -// relevant_regions.sort (layer_cmp); - - /* XXX this whole per-layer approach is a hack that - should be removed once Crossfades become - CrossfadeRegions and we just grab a list of relevant - regions and call read_at() on all of them. + /* Find all the regions that are involved in the bit we are reading, + and sort them by descending layer and ascending position. */ + boost::shared_ptr all = regions_touched (start, start + cnt - 1); + all->sort (ReadSorter ()); - sort (relevant_layers.begin(), relevant_layers.end()); - - for (vector::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) { - - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l)); - - vector > r (relevant_regions[*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); - } + /* This will be a list of the bits of our read range that we have + read completely (ie for which no more regions need to be read). + It is a list of ranges in session frames. + */ + Evoral::RangeList done; + + for (RegionList::iterator i = all->begin(); i != all->end(); ++i) { + boost::shared_ptr ar = boost::dynamic_pointer_cast (*i); - for (vector >::iterator i = x.begin(); i != x.end(); ++i) { - DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name())); - (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n); - } - } + /* Trim region range to the bit we are reading */ - return ret; -} + /* Work out which bits of this region need to be read; + first, trim to the range we are reading... + */ + Evoral::Range region_range = ar->range (); + region_range.from = max (region_range.from, start); + region_range.to = min (region_range.to, start + cnt - 1); + /* ... and then remove the bits that are already done */ + Evoral::RangeList to_do = Evoral::subtract (region_range, done); -void -AudioPlaylist::remove_dependents (boost::shared_ptr region) -{ - boost::shared_ptr r = boost::dynamic_pointer_cast (region); + /* Read those bits, adding their bodies (the parts between end-of-fade-in + and start-of-fade-out) to the `done' list. + */ - if (in_set_state) { - return; - } + Evoral::RangeList::List t = to_do.get (); - if (r == 0) { - fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist") - << endmsg; - return; - } + for (Evoral::RangeList::List::iterator i = t.begin(); i != t.end(); ++i) { + Evoral::Range d = *i; - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) { + /* Read the whole range, possibly including fades */ + ar->read_at (buf + d.from - start, mixdown_buffer, gain_buffer, d.from, d.to - d.from + 1, chan_n); - if ((*i)->involves (r)) { - i = _crossfades.erase (i); - } else { - ++i; + if (ar->opaque ()) { + /* Cut this range down to just the body and mark it done */ + Evoral::Range body = ar->body_range (); + if (body.from < d.to && body.to > d.from) { + d.from = max (d.from, body.from); + d.to = min (d.to, body.to); + done.add (d); + } + } } } -} - - -void -AudioPlaylist::flush_notifications (bool from_undo) -{ - Playlist::flush_notifications (from_undo); - if (in_flush) { - return; - } - - in_flush = true; - - Crossfades::iterator a; - for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) { - NewCrossfade (*a); /* EMIT SIGNAL */ - } - - _pending_xfade_adds.clear (); - - in_flush = false; + return cnt; } void -AudioPlaylist::refresh_dependents (boost::shared_ptr r) +AudioPlaylist::check_crossfades (Evoral::Range range) { - boost::shared_ptr ar = boost::dynamic_pointer_cast(r); - set > updated; - - if (ar == 0) { + if (in_set_state || in_partition || !_session.config.get_auto_xfade ()) { return; } + + boost::shared_ptr starts = regions_with_start_within (range); + boost::shared_ptr ends = regions_with_end_within (range); - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { - - Crossfades::iterator tmp; - - tmp = x; - ++tmp; + RegionList all = *starts; + std::copy (ends->begin(), ends->end(), back_inserter (all)); - /* only update them once */ + all.sort (RegionSortByLayer ()); - if ((*x)->involves (ar)) { + set > done_start; + set > done_end; - pair >::iterator, bool> const u = updated.insert (*x); + for (RegionList::reverse_iterator i = all.rbegin(); i != all.rend(); ++i) { + for (RegionList::reverse_iterator j = all.rbegin(); j != all.rend(); ++j) { - 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 - } + if (i == j) { + continue; } - } - - x = tmp; - } -} - -void -AudioPlaylist::finalize_split_region (boost::shared_ptr o, boost::shared_ptr l, boost::shared_ptr r) -{ - boost::shared_ptr orig = boost::dynamic_pointer_cast(o); - boost::shared_ptr left = boost::dynamic_pointer_cast(l); - boost::shared_ptr right = boost::dynamic_pointer_cast(r); - - for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) { - Crossfades::iterator tmp; - tmp = x; - ++tmp; - boost::shared_ptr fade; + if ((*i)->muted() || (*j)->muted()) { + continue; + } - if ((*x)->_in == orig) { - if (! (*x)->covers(right->position())) { - 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 = boost::shared_ptr (new Crossfade (*x, right, (*x)->_out)); + if ((*i)->position() == (*j)->position() && ((*i)->length() == (*j)->length())) { + /* precise overlay: no xfade */ + continue; } - } - if ((*x)->_out == orig) { - if (! (*x)->covers(right->position())) { - fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, right)); + if ((*i)->position() == (*j)->position() || ((*i)->last_frame() == (*j)->last_frame())) { + /* starts or ends match: no xfade */ + continue; + } + + + boost::shared_ptr top; + boost::shared_ptr bottom; + + if ((*i)->layer() < (*j)->layer()) { + top = boost::dynamic_pointer_cast (*j); + bottom = boost::dynamic_pointer_cast (*i); } else { - // Overlap, the crossfade is copied on the right side of the left region instead - fade = boost::shared_ptr (new Crossfade (*x, (*x)->_in, left)); + top = boost::dynamic_pointer_cast (*i); + bottom = boost::dynamic_pointer_cast (*j); + } + + if (!top->opaque ()) { + continue; } - } - - if (fade) { - _crossfades.remove (*x); - add_crossfade (fade); - } - x = tmp; - } -} - -void -AudioPlaylist::check_dependents (boost::shared_ptr r, bool norefresh) -{ - boost::shared_ptr other; - boost::shared_ptr region; - boost::shared_ptr top; - boost::shared_ptr bottom; - boost::shared_ptr xfade; - boost::shared_ptr touched_regions; - - if (in_set_state || in_partition) { - return; - } - - if ((region = boost::dynamic_pointer_cast (r)) == 0) { - fatal << _("programming error: non-audio Region tested for overlap in audio playlist") - << endmsg; - return; - } - - if (!norefresh) { - refresh_dependents (r); - } - - - 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) { - continue; - } - - if (other->muted() || region->muted()) { - continue; - } - - if (other->position() == r->position() && other->length() == r->length()) { - /* precise overlay of two regions - no xfade */ - continue; - } - - if (other->layer() < region->layer()) { - top = region; - bottom = other; - } else { - top = other; - bottom = region; - } - - if (!top->opaque()) { - continue; - } - - OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); - - touched_regions.reset (); - - try { - 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, StartOfIn)); - xfade->set_position (top->first_frame()); - 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). - */ - - xfade = boost::shared_ptr (new Crossfade (bottom, top, xfade_length, EndOfOut)); - xfade->set_position (top->last_frame() - xfade_length); - add_crossfade (xfade); - } - break; - case OverlapStart: - /* { ==== top ============ } - * [---- bottom -------------------] + Evoral::OverlapType const c = top->coverage (bottom->position(), bottom->last_frame()); + + if (c == Evoral::OverlapStart) { + + /* top starts within bottom but covers bottom's end */ + + /* { ==== 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 { - - 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); - } + if (done_start.find (top) == done_start.end() && done_end.find (bottom) == done_end.end ()) { + framecnt_t const len = bottom->last_frame () - top->first_frame (); + top->set_fade_in_length (len); + top->set_fade_in_active (true); + done_start.insert (top); + bottom->set_fade_out_length (len); + bottom->set_fade_out_active (true); + done_end.insert (bottom); } - break; - case OverlapEnd: - - /* [---- top ------------------------] - * { ==== bottom ============ } + } else if (c == Evoral::OverlapEnd) { + + /* top covers start of bottom but ends within it */ + + /* [---- 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); - } + if (done_end.find (top) == done_end.end() && done_start.find (bottom) == done_start.end ()) { + framecnt_t const len = top->last_frame () - bottom->first_frame (); + top->set_fade_out_length (len); + top->set_fade_out_active (true); + done_end.insert (top); + bottom->set_fade_in_length (len); + bottom->set_fade_in_active (true); + done_start.insert (bottom); } - 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; - } - - } -} - -void -AudioPlaylist::add_crossfade (boost::shared_ptr xfade) -{ - Crossfades::iterator ci; - - for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) { - if (*(*ci) == *xfade) { // Crossfade::operator==() - break; - } - } - - if (ci != _crossfades.end()) { - // it will just go away - } else { - _crossfades.push_back (xfade); - - 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); - } -} - -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); - } else { - NewCrossfade (x); /* EMIT SIGNAL */ - } -} - -void -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 (); - - if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) { - _crossfades.erase (i); } -} - -int -AudioPlaylist::set_state (const XMLNode& node, int version) -{ - XMLNode *child; - XMLNodeList nlist; - XMLNodeConstIterator niter; - - in_set_state++; - - if (Playlist::set_state (node, version)) { - return -1; - } - - freeze (); - - nlist = node.children(); - - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - - child = *niter; - - if (child->name() != "Crossfade") { - continue; - } - try { - boost::shared_ptr xfade = boost::shared_ptr (new Crossfade (*((const Playlist *)this), *child)); - _crossfades.push_back (xfade); - 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) - // << endl; - continue; + for (RegionList::iterator i = starts->begin(); i != starts->end(); ++i) { + if (done_start.find (*i) == done_start.end()) { + boost::shared_ptr r = boost::dynamic_pointer_cast (*i); + r->set_default_fade_in (); } } - thaw (); - in_set_state--; - - return 0; -} - -void -AudioPlaylist::clear (bool with_signals) -{ - _crossfades.clear (); - Playlist::clear (with_signals); -} - -XMLNode& -AudioPlaylist::state (bool full_state) -{ - XMLNode& node = Playlist::state (full_state); - - if (full_state) { - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - node.add_child_nocopy ((*i)->get_state()); + for (RegionList::iterator i = ends->begin(); i != ends->end(); ++i) { + if (done_end.find (*i) == done_end.end()) { + boost::shared_ptr r = boost::dynamic_pointer_cast (*i); + r->set_default_fade_out (); } } - - return node; } void AudioPlaylist::dump () const { boost::shared_ptrr; - boost::shared_ptr x; cerr << "Playlist \"" << _name << "\" " << endl << regions.size() << " regions " - << _crossfades.size() << " crossfades" << endl; for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) { @@ -791,21 +361,6 @@ AudioPlaylist::dump () const << r->layer () << endl; } - - for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - x = *i; - cerr << " xfade [" - << x->out()->name() - << ',' - << x->in()->name() - << " @ " - << x->position() - << " length = " - << x->length () - << " active ? " - << (x->active() ? "yes" : "no") - << endl; - } } bool @@ -818,8 +373,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) } bool changed = false; - Crossfades::iterator c, ctmp; - set > unique_xfades; { RegionLock rlock (this); @@ -853,18 +406,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) region->set_playlist (boost::shared_ptr()); } - for (c = _crossfades.begin(); c != _crossfades.end(); ) { - ctmp = c; - ++ctmp; - - if ((*c)->involves (r)) { - unique_xfades.insert (*c); - _crossfades.erase (c); - } - - c = ctmp; - } - if (changed) { /* overload this, it normally means "removed", not destroyed */ notify_region_removed (region); @@ -873,22 +414,6 @@ AudioPlaylist::destroy_region (boost::shared_ptr region) return changed; } -void -AudioPlaylist::crossfade_changed (const PropertyChange&) -{ - if (in_flush || in_set_state) { - return; - } - - /* XXX is there a loop here? can an xfade change not happen - due to a playlist change? well, sure activation would - be an example. maybe we should check the type of change - that occured. - */ - - notify_contents_changed (); -} - bool AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr region) { @@ -917,168 +442,6 @@ AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared return true; } -void -AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist) -{ - RegionLock rlock (this); - - for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - framepos_t const start = (*i)->position (); - framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference - - 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 */ -} - -boost::shared_ptr -AudioPlaylist::find_crossfade (const PBD::ID& id) const -{ - Crossfades::const_iterator i = _crossfades.begin (); - while (i != _crossfades.end() && (*i)->id() != id) { - ++i; - } - - if (i == _crossfades.end()) { - return boost::shared_ptr (); - } - - return *i; -} - -struct crossfade_triple { - boost::shared_ptr old_in; - boost::shared_ptr new_in; - boost::shared_ptr new_out; -}; - -void -AudioPlaylist::copy_dependents (const vector& old_and_new, Playlist* other) const -{ - AudioPlaylist* other_audio = dynamic_cast(other); - - if (!other_audio) { - return; - } - - /* our argument is a vector of old and new regions. Each old region - might be participant in a crossfade that is already present. Each new - region is a copy of the old region, present in the other playlist. - - our task is to find all the relevant xfades in our playlist (involving - the "old" regions) and place copies of them in the other playlist. - */ - - typedef map,crossfade_triple> CrossfadeInfo; - CrossfadeInfo crossfade_info; - - /* build up a record that links crossfades, old regions and new regions - */ - - for (vector::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) { - - for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - - if ((*i)->in() == on->first) { - - CrossfadeInfo::iterator cf; - - if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { - - /* already have a record for the old fade-in region, - so note the new fade-in region - */ - - cf->second.new_in = on->second; - - } else { - - /* add a record of this crossfade, keeping an association - with the new fade-in region - */ - - crossfade_triple ct; - - ct.old_in = on->first; - ct.new_in = on->second; - - crossfade_info[*i] = ct; - } - - } else if ((*i)->out() == on->first) { - - /* this old region is the fade-out region of this crossfade */ - - CrossfadeInfo::iterator cf; - - if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) { - - /* already have a record for this crossfade, so just keep - an association for the new fade out region - */ - - cf->second.new_out = on->second; - - } else { - - /* add a record of this crossfade, keeping an association - with the new fade-in region - */ - - crossfade_triple ct; - - ct.old_in = on->first; - ct.new_out = on->second; - - crossfade_info[*i] = ct; - } - } - } - } - - for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) { - - /* for each crossfade that involves at least two of the old regions, - create a new identical crossfade with the new regions - */ - - if (!ci->second.new_in || !ci->second.new_out) { - continue; - } - - boost::shared_ptr new_xfade (new Crossfade (ci->first, - boost::dynamic_pointer_cast(ci->second.new_in), - boost::dynamic_pointer_cast(ci->second.new_out))); - - /* add it at the right position - which must be at the start - * of the fade-in region - */ - - new_xfade->set_position (ci->second.new_in->position()); - other_audio->add_crossfade (new_xfade); - } -} - void AudioPlaylist::pre_combine (vector >& copies) { @@ -1195,12 +558,52 @@ AudioPlaylist::pre_uncombine (vector >& originals, boo } } -void -AudioPlaylist::get_equivalent_crossfades (boost::shared_ptr c, vector > & results) +int +AudioPlaylist::set_state (const XMLNode& node, int version) { - for (list >::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) { - if ((*i)->equivalent (c)) { - results.push_back (*i); + int const r = Playlist::set_state (node, version); + if (r) { + return r; + } + + /* Read legacy Crossfade nodes and set up region fades accordingly */ + + XMLNodeList children = node.children (); + for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) { + if ((*i)->name() == X_("Crossfade")) { + + XMLProperty* p = (*i)->property (X_("active")); + assert (p); + if (!string_is_affirmative (p->value())) { + continue; + } + + p = (*i)->property (X_("in")); + assert (p); + boost::shared_ptr in = region_by_id (PBD::ID (p->value ())); + assert (in); + boost::shared_ptr in_a = boost::dynamic_pointer_cast (in); + assert (in_a); + + p = (*i)->property (X_("out")); + assert (p); + boost::shared_ptr out = region_by_id (PBD::ID (p->value ())); + assert (out); + boost::shared_ptr out_a = boost::dynamic_pointer_cast (out); + assert (out_a); + + XMLNodeList c = (*i)->children (); + for (XMLNodeConstIterator j = c.begin(); j != c.end(); ++j) { + if ((*j)->name() == X_("FadeIn")) { + in_a->fade_in()->set_state (**j, version); + in_a->set_fade_in_active (true); + } else if ((*j)->name() == X_("FadeOut")) { + out_a->fade_out()->set_state (**j, version); + out_a->set_fade_out_active (true); + } + } } } + + return 0; } -- cgit v1.2.3