2 Copyright (C) 2003 Paul Davis
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.
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.
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.
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/crossfade.h"
30 #include "ardour/session.h"
31 #include "pbd/enumwriter.h"
35 using namespace ARDOUR;
40 namespace Properties {
41 PBD::PropertyDescriptor<bool> crossfades;
46 AudioPlaylist::make_property_quarks ()
48 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
49 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
52 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
53 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
59 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
60 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
61 , _playlist (p._playlist)
67 CrossfadeListProperty *
68 CrossfadeListProperty::create () const
70 return new CrossfadeListProperty (_playlist);
73 CrossfadeListProperty *
74 CrossfadeListProperty::clone () const
76 return new CrossfadeListProperty (*this);
80 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
82 /* Crossfades are not written to any state when they are no
83 longer in use, so we must write their state here.
86 XMLNode& c = xfade->get_state ();
87 node.add_child_nocopy (c);
90 boost::shared_ptr<Crossfade>
91 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
93 XMLNodeList const c = node.children ();
94 assert (c.size() == 1);
95 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
99 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
100 : Playlist (session, node, DataType::AUDIO, hidden)
101 , _crossfades (*this)
104 const XMLProperty* prop = node.property("type");
105 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
108 add_property (_crossfades);
111 set_state (node, Stateful::loading_state_version);
115 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
116 : Playlist (session, name, DataType::AUDIO, hidden)
117 , _crossfades (*this)
119 add_property (_crossfades);
122 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
123 : Playlist (other, name, hidden)
124 , _crossfades (*this)
126 add_property (_crossfades);
128 RegionList::const_iterator in_o = other->regions.begin();
129 RegionList::iterator in_n = regions.begin();
131 while (in_o != other->regions.end()) {
132 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
134 // We look only for crossfades which begin with the current region, so we don't get doubles
135 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
136 if ((*xfades)->in() == ar) {
137 // We found one! Now copy it!
139 RegionList::const_iterator out_o = other->regions.begin();
140 RegionList::const_iterator out_n = regions.begin();
142 while (out_o != other->regions.end()) {
144 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
146 if ((*xfades)->out() == ar2) {
147 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
148 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
149 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
150 add_crossfade(new_fade);
157 // cerr << "HUH!? second region in the crossfade not found!" << endl;
166 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
167 : Playlist (other, start, cnt, name, hidden)
168 , _crossfades (*this)
170 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
173 add_property (_crossfades);
175 framepos_t const end = start + cnt - 1;
177 /* Audio regions that have been created by the Playlist constructor
178 will currently have the same fade in/out as the regions that they
179 were created from. This is wrong, so reset the fades here.
182 RegionList::iterator ours = regions.begin ();
184 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
185 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
188 framecnt_t fade_in = 64;
189 framecnt_t fade_out = 64;
192 frameoffset_t offset;
194 switch (region->coverage (start, end)) {
198 case OverlapInternal:
200 framecnt_t const offset = start - region->position ();
201 framecnt_t const trim = region->last_frame() - end;
202 if (region->fade_in()->back()->when > offset) {
203 fade_in = region->fade_in()->back()->when - offset;
205 if (region->fade_out()->back()->when > trim) {
206 fade_out = region->fade_out()->back()->when - trim;
212 position = region->position() - start;
213 len = end - region->position();
215 if (end > region->position() + region->fade_in()->back()->when)
216 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
217 if (end > region->last_frame() - region->fade_out()->back()->when)
218 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
224 offset = start - region->position();
225 len = region->length() - offset;
227 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
228 fade_out = region->fade_out()->back()->when;
230 if (start < region->position() + region->fade_in()->back()->when)
231 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
235 case OverlapExternal:
236 fade_in = region->fade_in()->back()->when;
237 fade_out = region->fade_out()->back()->when;
241 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
244 our_region->set_fade_in_length (fade_in);
245 our_region->set_fade_out_length (fade_out);
251 /* this constructor does NOT notify others (session) */
254 AudioPlaylist::~AudioPlaylist ()
256 _crossfades.clear ();
259 struct RegionSortByLayer {
260 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
261 return a->layer() < b->layer();
266 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
267 framecnt_t cnt, unsigned chan_n)
269 framecnt_t ret = cnt;
271 /* optimizing this memset() away involves a lot of conditionals
272 that may well cause more of a hit due to cache misses
273 and related stuff than just doing this here.
275 it would be great if someone could measure this
278 one way or another, parts of the requested area
279 that are not written to by Region::region_at()
280 for all Regions that cover the area need to be
284 memset (buf, 0, sizeof (Sample) * cnt);
286 /* this function is never called from a realtime thread, so
287 its OK to block (for short intervals).
290 Glib::RecMutex::Lock rm (region_lock);
292 framepos_t const end = start + cnt - 1;
293 framecnt_t read_frames = 0;
294 framecnt_t skip_frames = 0;
295 _read_data_count = 0;
297 _read_data_count = 0;
299 RegionList* rlist = regions_to_read (start, start+cnt);
301 if (rlist->empty()) {
306 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
307 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
308 vector<uint32_t> relevant_layers;
310 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
311 if ((*i)->coverage (start, end) != OverlapNone) {
312 relevant_regions[(*i)->layer()].push_back (*i);
313 relevant_layers.push_back ((*i)->layer());
317 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
318 if ((*i)->coverage (start, end) != OverlapNone) {
319 relevant_xfades[(*i)->upper_layer()].push_back (*i);
323 // RegionSortByLayer layer_cmp;
324 // relevant_regions.sort (layer_cmp);
326 /* XXX this whole per-layer approach is a hack that
327 should be removed once Crossfades become
328 CrossfadeRegions and we just grab a list of relevant
329 regions and call read_at() on all of them.
332 sort (relevant_layers.begin(), relevant_layers.end());
334 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
336 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
337 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
340 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
341 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
342 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
344 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
345 _read_data_count += ar->read_data_count();
348 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
349 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
351 /* don't JACK up _read_data_count, since its the same data as we just
352 read from the regions, and the OS should handle that for us.
363 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
365 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
372 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
377 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
379 if ((*i)->involves (r)) {
380 i = _crossfades.erase (i);
389 AudioPlaylist::flush_notifications (bool from_undo)
391 Playlist::flush_notifications (from_undo);
399 Crossfades::iterator a;
400 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
401 NewCrossfade (*a); /* EMIT SIGNAL */
404 _pending_xfade_adds.clear ();
410 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
412 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
413 set<boost::shared_ptr<Crossfade> > updated;
419 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
421 Crossfades::iterator tmp;
426 /* only update them once */
428 if ((*x)->involves (ar)) {
430 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
433 /* x was successfully inserted into the set, so it has not already been updated */
438 catch (Crossfade::NoCrossfadeHere& err) {
439 // relax, Invalidated during refresh
449 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
451 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
452 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
453 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
455 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
456 Crossfades::iterator tmp;
460 boost::shared_ptr<Crossfade> fade;
462 if ((*x)->_in == orig) {
463 if (! (*x)->covers(right->position())) {
464 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
466 // Overlap, the crossfade is copied on the left side of the right region instead
467 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
471 if ((*x)->_out == orig) {
472 if (! (*x)->covers(right->position())) {
473 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
475 // Overlap, the crossfade is copied on the right side of the left region instead
476 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
481 _crossfades.remove (*x);
482 add_crossfade (fade);
489 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
491 boost::shared_ptr<AudioRegion> other;
492 boost::shared_ptr<AudioRegion> region;
493 boost::shared_ptr<AudioRegion> top;
494 boost::shared_ptr<AudioRegion> bottom;
495 boost::shared_ptr<Crossfade> xfade;
496 RegionList* touched_regions = 0;
498 if (in_set_state || in_partition) {
502 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
503 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
509 refresh_dependents (r);
513 if (!_session.config.get_auto_xfade()) {
517 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
518 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
520 if (other == region) {
524 if (other->muted() || region->muted()) {
528 if (other->position() == r->position() && other->length() == r->length()) {
529 /* precise overlay of two regions - no xfade */
533 if (other->layer() < region->layer()) {
541 if (!top->opaque()) {
545 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
547 delete touched_regions;
551 framecnt_t xfade_length;
556 case OverlapInternal:
557 /* {=============== top =============}
558 * [ ----- bottom ------- ]
562 case OverlapExternal:
564 /* [ -------- top ------- ]
565 * {=========== bottom =============}
568 /* to avoid discontinuities at the region boundaries of an internal
569 overlap (this region is completely within another), we create
570 two hidden crossfades at each boundary. this is not dependent
571 on the auto-xfade option, because we require it as basic
575 xfade_length = min ((framecnt_t) 720, top->length());
577 if (top_region_at (top->first_frame()) == top) {
579 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
580 add_crossfade (xfade);
583 if (top_region_at (top->last_frame() - 1) == top) {
586 only add a fade out if there is no region on top of the end of 'top' (which
590 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
591 add_crossfade (xfade);
596 /* { ==== top ============ }
597 * [---- bottom -------------------]
600 if (_session.config.get_xfade_model() == FullCrossfade) {
601 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
602 if (touched_regions->size() <= 2) {
603 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
604 add_crossfade (xfade);
608 touched_regions = regions_touched (top->first_frame(),
609 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
611 if (touched_regions->size() <= 2) {
612 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
613 add_crossfade (xfade);
620 /* [---- top ------------------------]
621 * { ==== bottom ============ }
624 if (_session.config.get_xfade_model() == FullCrossfade) {
626 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
627 if (touched_regions->size() <= 2) {
628 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
629 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
630 add_crossfade (xfade);
634 touched_regions = regions_touched (bottom->first_frame(),
635 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
637 if (touched_regions->size() <= 2) {
638 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
639 add_crossfade (xfade);
644 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
645 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
646 add_crossfade (xfade);
650 catch (failed_constructor& err) {
654 catch (Crossfade::NoCrossfadeHere& err) {
660 delete touched_regions;
664 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
666 Crossfades::iterator ci;
668 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
669 if (*(*ci) == *xfade) { // Crossfade::operator==()
674 if (ci != _crossfades.end()) {
675 // it will just go away
677 _crossfades.push_back (xfade);
679 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
680 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
682 notify_crossfade_added (xfade);
686 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
688 if (g_atomic_int_get(&block_notifications)) {
689 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
691 NewCrossfade (x); /* EMIT SIGNAL */
696 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
698 Crossfades::iterator i;
699 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
701 xfade->in()->resume_fade_in ();
702 xfade->out()->resume_fade_out ();
704 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
705 _crossfades.erase (i);
710 AudioPlaylist::set_state (const XMLNode& node, int version)
714 XMLNodeConstIterator niter;
718 Playlist::set_state (node, version);
722 nlist = node.children();
724 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
728 if (child->name() != "Crossfade") {
733 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
734 _crossfades.push_back (xfade);
735 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
736 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
740 catch (failed_constructor& err) {
741 // cout << string_compose (_("could not create crossfade object in playlist %1"),
755 AudioPlaylist::clear (bool with_signals)
757 _crossfades.clear ();
758 Playlist::clear (with_signals);
762 AudioPlaylist::state (bool full_state)
764 XMLNode& node = Playlist::state (full_state);
767 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
768 node.add_child_nocopy ((*i)->get_state());
776 AudioPlaylist::dump () const
778 boost::shared_ptr<Region>r;
779 boost::shared_ptr<Crossfade> x;
781 cerr << "Playlist \"" << _name << "\" " << endl
782 << regions.size() << " regions "
783 << _crossfades.size() << " crossfades"
786 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
788 cerr << " " << r->name() << " @ " << r << " ["
789 << r->start() << "+" << r->length()
797 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
808 << (x->active() ? "yes" : "no")
814 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
816 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
822 bool changed = false;
823 Crossfades::iterator c, ctmp;
824 set<boost::shared_ptr<Crossfade> > unique_xfades;
827 RegionLock rlock (this);
829 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
831 RegionList::iterator tmp = i;
834 if ((*i) == region) {
842 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
844 set<boost::shared_ptr<Region> >::iterator xtmp = x;
847 if ((*x) == region) {
848 all_regions.erase (x);
855 region->set_playlist (boost::shared_ptr<Playlist>());
858 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
862 if ((*c)->involves (r)) {
863 unique_xfades.insert (*c);
864 _crossfades.erase (c);
871 /* overload this, it normally means "removed", not destroyed */
872 notify_region_removed (region);
879 AudioPlaylist::crossfade_changed (const PropertyChange&)
881 if (in_flush || in_set_state) {
885 /* XXX is there a loop here? can an xfade change not happen
886 due to a playlist change? well, sure activation would
887 be an example. maybe we should check the type of change
891 notify_contents_changed ();
895 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
897 if (in_flush || in_set_state) {
901 PropertyChange our_interests;
903 our_interests.add (Properties::fade_in_active);
904 our_interests.add (Properties::fade_out_active);
905 our_interests.add (Properties::scale_amplitude);
906 our_interests.add (Properties::envelope_active);
907 our_interests.add (Properties::envelope);
908 our_interests.add (Properties::fade_in);
909 our_interests.add (Properties::fade_out);
911 bool parent_wants_notify;
913 parent_wants_notify = Playlist::region_changed (what_changed, region);
915 if (parent_wants_notify || (what_changed.contains (our_interests))) {
916 notify_contents_changed ();
923 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
925 RegionLock rlock (this);
927 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
928 framepos_t const start = (*i)->position ();
929 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
931 if (frame >= start && frame <= end) {
932 clist.push_back (*i);
938 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
940 RegionLock rl (this, false);
941 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
947 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
949 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
953 /* don't remove crossfades here; they will be dealt with by the dependency code */
956 boost::shared_ptr<Crossfade>
957 AudioPlaylist::find_crossfade (const PBD::ID& id) const
959 Crossfades::const_iterator i = _crossfades.begin ();
960 while (i != _crossfades.end() && (*i)->id() != id) {
964 if (i == _crossfades.end()) {
965 return boost::shared_ptr<Crossfade> ();
971 struct crossfade_triple {
972 boost::shared_ptr<Region> old_in;
973 boost::shared_ptr<Region> new_in;
974 boost::shared_ptr<Region> new_out;
978 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, boost::shared_ptr<Playlist> other)
980 boost::shared_ptr<AudioPlaylist> other_audio = boost::dynamic_pointer_cast<AudioPlaylist>(other);
986 /* our argument is a vector of old and new regions. Each old region
987 might be participant in a crossfade that is already present. Each new
988 region is a copy of the old region, present in the other playlist.
990 our task is to find all the relevant xfades in our playlist (involving
991 the "old" regions) and place copies of them in the other playlist.
994 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
995 CrossfadeInfo crossfade_info;
997 /* build up a record that links crossfades, old regions and new regions
1000 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1002 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1004 if ((*i)->in() == on->first) {
1006 CrossfadeInfo::iterator cf;
1008 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1010 /* already have a record for the old fade-in region,
1011 so note the new fade-in region
1014 cf->second.new_in = on->second;
1018 /* add a record of this crossfade, keeping an association
1019 with the new fade-in region
1022 crossfade_triple ct;
1024 ct.old_in = on->first;
1025 ct.new_in = on->second;
1027 crossfade_info[*i] = ct;
1030 } else if ((*i)->out() == on->first) {
1032 /* this old region is the fade-out region of this crossfade */
1034 CrossfadeInfo::iterator cf;
1036 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1038 /* already have a record for this crossfade, so just keep
1039 an association for the new fade out region
1042 cf->second.new_out = on->second;
1046 /* add a record of this crossfade, keeping an association
1047 with the new fade-in region
1050 crossfade_triple ct;
1052 ct.old_in = on->first;
1053 ct.new_out = on->second;
1055 crossfade_info[*i] = ct;
1061 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1063 /* for each crossfade that involves at least two of the old regions,
1064 create a new identical crossfade with the new regions
1067 if (!ci->second.new_in || !ci->second.new_out) {
1071 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1072 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1073 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1075 /* add it at the right position */
1077 new_xfade->set_position (ci->second.new_in->position(), this);
1078 other_audio->add_crossfade (new_xfade);