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/region_sorters.h"
31 #include "ardour/session.h"
32 #include "pbd/enumwriter.h"
36 using namespace ARDOUR;
41 namespace Properties {
42 PBD::PropertyDescriptor<bool> crossfades;
47 AudioPlaylist::make_property_quarks ()
49 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
50 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
53 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
54 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
60 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
61 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
62 , _playlist (p._playlist)
68 CrossfadeListProperty *
69 CrossfadeListProperty::create () const
71 return new CrossfadeListProperty (_playlist);
74 CrossfadeListProperty *
75 CrossfadeListProperty::clone () const
77 return new CrossfadeListProperty (*this);
81 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
83 /* Crossfades are not written to any state when they are no
84 longer in use, so we must write their state here.
87 XMLNode& c = xfade->get_state ();
88 node.add_child_nocopy (c);
91 boost::shared_ptr<Crossfade>
92 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
94 XMLNodeList const c = node.children ();
95 assert (c.size() == 1);
96 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
100 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
101 : Playlist (session, node, DataType::AUDIO, hidden)
102 , _crossfades (*this)
105 const XMLProperty* prop = node.property("type");
106 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
109 add_property (_crossfades);
112 set_state (node, Stateful::loading_state_version);
116 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
117 : Playlist (session, name, DataType::AUDIO, hidden)
118 , _crossfades (*this)
120 add_property (_crossfades);
123 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
124 : Playlist (other, name, hidden)
125 , _crossfades (*this)
127 add_property (_crossfades);
129 RegionList::const_iterator in_o = other->regions.begin();
130 RegionList::iterator in_n = regions.begin();
132 while (in_o != other->regions.end()) {
133 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
135 // We look only for crossfades which begin with the current region, so we don't get doubles
136 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
137 if ((*xfades)->in() == ar) {
138 // We found one! Now copy it!
140 RegionList::const_iterator out_o = other->regions.begin();
141 RegionList::const_iterator out_n = regions.begin();
143 while (out_o != other->regions.end()) {
145 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
147 if ((*xfades)->out() == ar2) {
148 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
149 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
150 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
151 add_crossfade(new_fade);
158 // cerr << "HUH!? second region in the crossfade not found!" << endl;
167 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
168 : Playlist (other, start, cnt, name, hidden)
169 , _crossfades (*this)
171 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
174 add_property (_crossfades);
176 framepos_t const end = start + cnt - 1;
178 /* Audio regions that have been created by the Playlist constructor
179 will currently have the same fade in/out as the regions that they
180 were created from. This is wrong, so reset the fades here.
183 RegionList::iterator ours = regions.begin ();
185 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
186 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
189 framecnt_t fade_in = 64;
190 framecnt_t fade_out = 64;
193 frameoffset_t offset;
195 switch (region->coverage (start, end)) {
199 case OverlapInternal:
201 framecnt_t const offset = start - region->position ();
202 framecnt_t const trim = region->last_frame() - end;
203 if (region->fade_in()->back()->when > offset) {
204 fade_in = region->fade_in()->back()->when - offset;
206 if (region->fade_out()->back()->when > trim) {
207 fade_out = region->fade_out()->back()->when - trim;
213 position = region->position() - start;
214 len = end - region->position();
216 if (end > region->position() + region->fade_in()->back()->when)
217 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
218 if (end > region->last_frame() - region->fade_out()->back()->when)
219 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
225 offset = start - region->position();
226 len = region->length() - offset;
228 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
229 fade_out = region->fade_out()->back()->when;
231 if (start < region->position() + region->fade_in()->back()->when)
232 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
236 case OverlapExternal:
237 fade_in = region->fade_in()->back()->when;
238 fade_out = region->fade_out()->back()->when;
242 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
245 our_region->set_fade_in_length (fade_in);
246 our_region->set_fade_out_length (fade_out);
252 /* this constructor does NOT notify others (session) */
255 AudioPlaylist::~AudioPlaylist ()
257 _crossfades.clear ();
260 struct RegionSortByLayer {
261 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
262 return a->layer() < b->layer();
267 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
268 framecnt_t cnt, unsigned chan_n)
270 framecnt_t ret = cnt;
272 /* optimizing this memset() away involves a lot of conditionals
273 that may well cause more of a hit due to cache misses
274 and related stuff than just doing this here.
276 it would be great if someone could measure this
279 one way or another, parts of the requested area
280 that are not written to by Region::region_at()
281 for all Regions that cover the area need to be
285 memset (buf, 0, sizeof (Sample) * cnt);
287 /* this function is never called from a realtime thread, so
288 its OK to block (for short intervals).
291 Glib::RecMutex::Lock rm (region_lock);
293 framepos_t const end = start + cnt - 1;
294 framecnt_t read_frames = 0;
295 framecnt_t skip_frames = 0;
296 _read_data_count = 0;
298 _read_data_count = 0;
300 RegionList* rlist = regions_to_read (start, start+cnt);
302 if (rlist->empty()) {
307 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
308 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
309 vector<uint32_t> relevant_layers;
311 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
312 if ((*i)->coverage (start, end) != OverlapNone) {
313 relevant_regions[(*i)->layer()].push_back (*i);
314 relevant_layers.push_back ((*i)->layer());
318 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
319 if ((*i)->coverage (start, end) != OverlapNone) {
320 relevant_xfades[(*i)->upper_layer()].push_back (*i);
324 // RegionSortByLayer layer_cmp;
325 // relevant_regions.sort (layer_cmp);
327 /* XXX this whole per-layer approach is a hack that
328 should be removed once Crossfades become
329 CrossfadeRegions and we just grab a list of relevant
330 regions and call read_at() on all of them.
333 sort (relevant_layers.begin(), relevant_layers.end());
335 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
337 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
338 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
341 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
342 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
343 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
345 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
346 _read_data_count += ar->read_data_count();
349 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
350 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
352 /* don't JACK up _read_data_count, since its the same data as we just
353 read from the regions, and the OS should handle that for us.
364 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
366 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
373 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
378 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
380 if ((*i)->involves (r)) {
381 i = _crossfades.erase (i);
390 AudioPlaylist::flush_notifications (bool from_undo)
392 Playlist::flush_notifications (from_undo);
400 Crossfades::iterator a;
401 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
402 NewCrossfade (*a); /* EMIT SIGNAL */
405 _pending_xfade_adds.clear ();
411 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
413 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
414 set<boost::shared_ptr<Crossfade> > updated;
420 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
422 Crossfades::iterator tmp;
427 /* only update them once */
429 if ((*x)->involves (ar)) {
431 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
434 /* x was successfully inserted into the set, so it has not already been updated */
439 catch (Crossfade::NoCrossfadeHere& err) {
440 // relax, Invalidated during refresh
450 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
452 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
453 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
454 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
456 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
457 Crossfades::iterator tmp;
461 boost::shared_ptr<Crossfade> fade;
463 if ((*x)->_in == orig) {
464 if (! (*x)->covers(right->position())) {
465 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
467 // Overlap, the crossfade is copied on the left side of the right region instead
468 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
472 if ((*x)->_out == orig) {
473 if (! (*x)->covers(right->position())) {
474 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
476 // Overlap, the crossfade is copied on the right side of the left region instead
477 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
482 _crossfades.remove (*x);
483 add_crossfade (fade);
490 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
492 boost::shared_ptr<AudioRegion> other;
493 boost::shared_ptr<AudioRegion> region;
494 boost::shared_ptr<AudioRegion> top;
495 boost::shared_ptr<AudioRegion> bottom;
496 boost::shared_ptr<Crossfade> xfade;
497 RegionList* touched_regions = 0;
499 if (in_set_state || in_partition) {
503 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
504 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
510 refresh_dependents (r);
514 if (!_session.config.get_auto_xfade()) {
518 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
519 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
521 if (other == region) {
525 if (other->muted() || region->muted()) {
529 if (other->position() == r->position() && other->length() == r->length()) {
530 /* precise overlay of two regions - no xfade */
534 if (other->layer() < region->layer()) {
542 if (!top->opaque()) {
546 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
548 delete touched_regions;
552 framecnt_t xfade_length;
557 case OverlapInternal:
558 /* {=============== top =============}
559 * [ ----- bottom ------- ]
563 case OverlapExternal:
565 /* [ -------- top ------- ]
566 * {=========== bottom =============}
569 /* to avoid discontinuities at the region boundaries of an internal
570 overlap (this region is completely within another), we create
571 two hidden crossfades at each boundary. this is not dependent
572 on the auto-xfade option, because we require it as basic
576 xfade_length = min ((framecnt_t) 720, top->length());
578 if (top_region_at (top->first_frame()) == top) {
580 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
581 add_crossfade (xfade);
584 if (top_region_at (top->last_frame() - 1) == top) {
587 only add a fade out if there is no region on top of the end of 'top' (which
591 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
592 add_crossfade (xfade);
597 /* { ==== top ============ }
598 * [---- bottom -------------------]
601 if (_session.config.get_xfade_model() == FullCrossfade) {
602 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
603 if (touched_regions->size() <= 2) {
604 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
605 add_crossfade (xfade);
609 touched_regions = regions_touched (top->first_frame(),
610 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
612 if (touched_regions->size() <= 2) {
613 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
614 add_crossfade (xfade);
621 /* [---- top ------------------------]
622 * { ==== bottom ============ }
625 if (_session.config.get_xfade_model() == FullCrossfade) {
627 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
628 if (touched_regions->size() <= 2) {
629 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
630 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
631 add_crossfade (xfade);
635 touched_regions = regions_touched (bottom->first_frame(),
636 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
638 if (touched_regions->size() <= 2) {
639 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
640 add_crossfade (xfade);
645 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
646 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
647 add_crossfade (xfade);
651 catch (failed_constructor& err) {
655 catch (Crossfade::NoCrossfadeHere& err) {
661 delete touched_regions;
665 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
667 Crossfades::iterator ci;
669 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
670 if (*(*ci) == *xfade) { // Crossfade::operator==()
675 if (ci != _crossfades.end()) {
676 // it will just go away
678 _crossfades.push_back (xfade);
680 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
681 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
683 notify_crossfade_added (xfade);
687 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
689 if (g_atomic_int_get(&block_notifications)) {
690 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
692 NewCrossfade (x); /* EMIT SIGNAL */
697 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
699 Crossfades::iterator i;
700 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
702 xfade->in()->resume_fade_in ();
703 xfade->out()->resume_fade_out ();
705 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
706 _crossfades.erase (i);
711 AudioPlaylist::set_state (const XMLNode& node, int version)
715 XMLNodeConstIterator niter;
719 Playlist::set_state (node, version);
723 nlist = node.children();
725 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
729 if (child->name() != "Crossfade") {
734 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
735 _crossfades.push_back (xfade);
736 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
737 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
741 catch (failed_constructor& err) {
742 // cout << string_compose (_("could not create crossfade object in playlist %1"),
756 AudioPlaylist::clear (bool with_signals)
758 _crossfades.clear ();
759 Playlist::clear (with_signals);
763 AudioPlaylist::state (bool full_state)
765 XMLNode& node = Playlist::state (full_state);
768 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
769 node.add_child_nocopy ((*i)->get_state());
777 AudioPlaylist::dump () const
779 boost::shared_ptr<Region>r;
780 boost::shared_ptr<Crossfade> x;
782 cerr << "Playlist \"" << _name << "\" " << endl
783 << regions.size() << " regions "
784 << _crossfades.size() << " crossfades"
787 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
789 cerr << " " << r->name() << " @ " << r << " ["
790 << r->start() << "+" << r->length()
798 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
809 << (x->active() ? "yes" : "no")
815 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
817 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
823 bool changed = false;
824 Crossfades::iterator c, ctmp;
825 set<boost::shared_ptr<Crossfade> > unique_xfades;
828 RegionLock rlock (this);
830 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
832 RegionList::iterator tmp = i;
835 if ((*i) == region) {
843 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
845 set<boost::shared_ptr<Region> >::iterator xtmp = x;
848 if ((*x) == region) {
849 all_regions.erase (x);
856 region->set_playlist (boost::shared_ptr<Playlist>());
859 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
863 if ((*c)->involves (r)) {
864 unique_xfades.insert (*c);
865 _crossfades.erase (c);
872 /* overload this, it normally means "removed", not destroyed */
873 notify_region_removed (region);
880 AudioPlaylist::crossfade_changed (const PropertyChange&)
882 if (in_flush || in_set_state) {
886 /* XXX is there a loop here? can an xfade change not happen
887 due to a playlist change? well, sure activation would
888 be an example. maybe we should check the type of change
892 notify_contents_changed ();
896 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
898 if (in_flush || in_set_state) {
902 PropertyChange our_interests;
904 our_interests.add (Properties::fade_in_active);
905 our_interests.add (Properties::fade_out_active);
906 our_interests.add (Properties::scale_amplitude);
907 our_interests.add (Properties::envelope_active);
908 our_interests.add (Properties::envelope);
909 our_interests.add (Properties::fade_in);
910 our_interests.add (Properties::fade_out);
912 bool parent_wants_notify;
914 parent_wants_notify = Playlist::region_changed (what_changed, region);
916 if (parent_wants_notify || (what_changed.contains (our_interests))) {
917 notify_contents_changed ();
924 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
926 RegionLock rlock (this);
928 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
929 framepos_t const start = (*i)->position ();
930 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
932 if (frame >= start && frame <= end) {
933 clist.push_back (*i);
939 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
941 RegionLock rl (this, false);
942 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
948 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
950 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
954 /* don't remove crossfades here; they will be dealt with by the dependency code */
957 boost::shared_ptr<Crossfade>
958 AudioPlaylist::find_crossfade (const PBD::ID& id) const
960 Crossfades::const_iterator i = _crossfades.begin ();
961 while (i != _crossfades.end() && (*i)->id() != id) {
965 if (i == _crossfades.end()) {
966 return boost::shared_ptr<Crossfade> ();
972 struct crossfade_triple {
973 boost::shared_ptr<Region> old_in;
974 boost::shared_ptr<Region> new_in;
975 boost::shared_ptr<Region> new_out;
979 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, boost::shared_ptr<Playlist> other)
981 boost::shared_ptr<AudioPlaylist> other_audio = boost::dynamic_pointer_cast<AudioPlaylist>(other);
987 /* our argument is a vector of old and new regions. Each old region
988 might be participant in a crossfade that is already present. Each new
989 region is a copy of the old region, present in the other playlist.
991 our task is to find all the relevant xfades in our playlist (involving
992 the "old" regions) and place copies of them in the other playlist.
995 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
996 CrossfadeInfo crossfade_info;
998 /* build up a record that links crossfades, old regions and new regions
1001 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1003 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1005 if ((*i)->in() == on->first) {
1007 CrossfadeInfo::iterator cf;
1009 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1011 /* already have a record for the old fade-in region,
1012 so note the new fade-in region
1015 cf->second.new_in = on->second;
1019 /* add a record of this crossfade, keeping an association
1020 with the new fade-in region
1023 crossfade_triple ct;
1025 ct.old_in = on->first;
1026 ct.new_in = on->second;
1028 crossfade_info[*i] = ct;
1031 } else if ((*i)->out() == on->first) {
1033 /* this old region is the fade-out region of this crossfade */
1035 CrossfadeInfo::iterator cf;
1037 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1039 /* already have a record for this crossfade, so just keep
1040 an association for the new fade out region
1043 cf->second.new_out = on->second;
1047 /* add a record of this crossfade, keeping an association
1048 with the new fade-in region
1051 crossfade_triple ct;
1053 ct.old_in = on->first;
1054 ct.new_out = on->second;
1056 crossfade_info[*i] = ct;
1062 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1064 /* for each crossfade that involves at least two of the old regions,
1065 create a new identical crossfade with the new regions
1068 if (!ci->second.new_in || !ci->second.new_out) {
1072 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1073 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1074 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1076 /* add it at the right position - which must be at the start
1077 * of the fade-in region
1080 new_xfade->set_position (ci->second.new_in->position(), this);
1081 other_audio->add_crossfade (new_xfade);
1086 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1088 /* sort the originals into time order */
1090 RegionSortByPosition cmp;
1091 boost::shared_ptr<AudioRegion> ar;
1092 boost::shared_ptr<AudioRegion> cr;
1094 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1098 sort (originals.begin(), originals.end(), cmp);
1100 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1102 /* copy the fade in of the first into the compound region */
1105 cr->set_fade_in (ar->fade_in());
1107 /* disable the fade in of the first */
1109 ar->set_fade_in_active (false);
1112 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1115 /* copy the fade out of the last into the compound region */
1116 cr->set_fade_out (ar->fade_out());
1117 /* disable the fade out of the first */
1118 ar->set_fade_out_active (false);
1123 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1125 boost::shared_ptr<AudioRegion> ar;
1126 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1132 /* no need to call clear_changes() on the originals because that is
1133 * done within Playlist::uncombine ()
1136 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front())) != 0) {
1138 /* copy the compound region's fade in back into the first
1142 if (cr->fade_in()->back()->when <= ar->length()) {
1143 /* don't do this if the fade is longer than the
1146 ar->set_fade_in (cr->fade_in());
1149 ar->set_fade_in_active (true);
1150 _session.add_command (new StatefulDiffCommand (originals.front()));
1153 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back())) != 0) {
1155 /* copy the compound region's fade out back into the last
1159 if (cr->fade_out()->back()->when <= ar->length()) {
1160 /* don't do this if the fade is longer than the
1163 ar->set_fade_out (cr->fade_out());
1166 ar->set_fade_out_active (true);
1167 _session.add_command (new StatefulDiffCommand (originals.back()));