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 if (set_state (node, Stateful::loading_state_version)) {
113 throw failed_constructor();
118 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
119 : Playlist (session, name, DataType::AUDIO, hidden)
120 , _crossfades (*this)
122 add_property (_crossfades);
125 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
126 : Playlist (other, name, hidden)
127 , _crossfades (*this)
129 add_property (_crossfades);
131 RegionList::const_iterator in_o = other->regions.begin();
132 RegionList::iterator in_n = regions.begin();
134 while (in_o != other->regions.end()) {
135 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
137 // We look only for crossfades which begin with the current region, so we don't get doubles
138 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
139 if ((*xfades)->in() == ar) {
140 // We found one! Now copy it!
142 RegionList::const_iterator out_o = other->regions.begin();
143 RegionList::const_iterator out_n = regions.begin();
145 while (out_o != other->regions.end()) {
147 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
149 if ((*xfades)->out() == ar2) {
150 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
151 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
152 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
153 add_crossfade(new_fade);
160 // cerr << "HUH!? second region in the crossfade not found!" << endl;
169 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
170 : Playlist (other, start, cnt, name, hidden)
171 , _crossfades (*this)
173 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
176 add_property (_crossfades);
178 framepos_t const end = start + cnt - 1;
180 /* Audio regions that have been created by the Playlist constructor
181 will currently have the same fade in/out as the regions that they
182 were created from. This is wrong, so reset the fades here.
185 RegionList::iterator ours = regions.begin ();
187 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
188 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
191 framecnt_t fade_in = 64;
192 framecnt_t fade_out = 64;
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 if (end > region->position() + region->fade_in()->back()->when)
213 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
214 if (end > region->last_frame() - region->fade_out()->back()->when)
215 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
220 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
221 fade_out = region->fade_out()->back()->when;
223 if (start < region->position() + region->fade_in()->back()->when)
224 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
228 case OverlapExternal:
229 fade_in = region->fade_in()->back()->when;
230 fade_out = region->fade_out()->back()->when;
234 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
237 our_region->set_fade_in_length (fade_in);
238 our_region->set_fade_out_length (fade_out);
244 /* this constructor does NOT notify others (session) */
247 AudioPlaylist::~AudioPlaylist ()
249 _crossfades.clear ();
252 struct RegionSortByLayer {
253 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
254 return a->layer() < b->layer();
259 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
260 framecnt_t cnt, unsigned chan_n)
262 framecnt_t ret = cnt;
264 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n",
265 name(), start, cnt, chan_n, regions.size(), _crossfades.size()));
267 /* optimizing this memset() away involves a lot of conditionals
268 that may well cause more of a hit due to cache misses
269 and related stuff than just doing this here.
271 it would be great if someone could measure this
274 one way or another, parts of the requested area
275 that are not written to by Region::region_at()
276 for all Regions that cover the area need to be
280 memset (buf, 0, sizeof (Sample) * cnt);
282 /* this function is never called from a realtime thread, so
283 its OK to block (for short intervals).
286 Glib::RecMutex::Lock rm (region_lock);
288 framepos_t const end = start + cnt - 1;
289 _read_data_count = 0;
291 _read_data_count = 0;
293 RegionList* rlist = regions_to_read (start, start+cnt);
295 if (rlist->empty()) {
300 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
301 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
302 vector<uint32_t> relevant_layers;
304 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
305 if ((*i)->coverage (start, end) != OverlapNone) {
306 relevant_regions[(*i)->layer()].push_back (*i);
307 relevant_layers.push_back ((*i)->layer());
311 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
313 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
314 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ...\n",
315 name(), (*i)->out()->name(), (*i)->in()->name()));
316 if ((*i)->coverage (start, end) != OverlapNone) {
317 relevant_xfades[(*i)->upper_layer()].push_back (*i);
318 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant, place on layer %1\n",
319 (*i)->upper_layer()));
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 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
338 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
339 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
342 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
343 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
344 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
346 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
347 _read_data_count += ar->read_data_count();
350 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
351 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
352 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
354 /* don't JACK up _read_data_count, since its the same data as we just
355 read from the regions, and the OS should handle that for us.
366 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
368 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
375 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
380 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
382 if ((*i)->involves (r)) {
383 i = _crossfades.erase (i);
392 AudioPlaylist::flush_notifications (bool from_undo)
394 Playlist::flush_notifications (from_undo);
402 Crossfades::iterator a;
403 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
404 NewCrossfade (*a); /* EMIT SIGNAL */
407 _pending_xfade_adds.clear ();
413 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
415 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
416 set<boost::shared_ptr<Crossfade> > updated;
422 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
424 Crossfades::iterator tmp;
429 /* only update them once */
431 if ((*x)->involves (ar)) {
433 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
436 /* x was successfully inserted into the set, so it has not already been updated */
441 catch (Crossfade::NoCrossfadeHere& err) {
442 // relax, Invalidated during refresh
452 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
454 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
455 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
456 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
458 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
459 Crossfades::iterator tmp;
463 boost::shared_ptr<Crossfade> fade;
465 if ((*x)->_in == orig) {
466 if (! (*x)->covers(right->position())) {
467 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
469 // Overlap, the crossfade is copied on the left side of the right region instead
470 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
474 if ((*x)->_out == orig) {
475 if (! (*x)->covers(right->position())) {
476 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
478 // Overlap, the crossfade is copied on the right side of the left region instead
479 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
484 _crossfades.remove (*x);
485 add_crossfade (fade);
492 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
494 boost::shared_ptr<AudioRegion> other;
495 boost::shared_ptr<AudioRegion> region;
496 boost::shared_ptr<AudioRegion> top;
497 boost::shared_ptr<AudioRegion> bottom;
498 boost::shared_ptr<Crossfade> xfade;
499 RegionList* touched_regions = 0;
501 if (in_set_state || in_partition) {
505 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
506 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
512 refresh_dependents (r);
516 if (!_session.config.get_auto_xfade()) {
520 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
521 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
523 if (other == region) {
527 if (other->muted() || region->muted()) {
531 if (other->position() == r->position() && other->length() == r->length()) {
532 /* precise overlay of two regions - no xfade */
536 if (other->layer() < region->layer()) {
544 if (!top->opaque()) {
548 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
550 delete touched_regions;
554 framecnt_t xfade_length;
559 case OverlapInternal:
560 /* {=============== top =============}
561 * [ ----- bottom ------- ]
565 case OverlapExternal:
567 /* [ -------- top ------- ]
568 * {=========== bottom =============}
571 /* to avoid discontinuities at the region boundaries of an internal
572 overlap (this region is completely within another), we create
573 two hidden crossfades at each boundary. this is not dependent
574 on the auto-xfade option, because we require it as basic
578 xfade_length = min ((framecnt_t) 720, top->length());
580 if (top_region_at (top->first_frame()) == top) {
582 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
583 add_crossfade (xfade);
586 if (top_region_at (top->last_frame() - 1) == top) {
589 only add a fade out if there is no region on top of the end of 'top' (which
593 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
594 add_crossfade (xfade);
599 /* { ==== top ============ }
600 * [---- bottom -------------------]
603 if (_session.config.get_xfade_model() == FullCrossfade) {
604 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
605 if (touched_regions->size() <= 2) {
606 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
607 add_crossfade (xfade);
611 touched_regions = regions_touched (top->first_frame(),
612 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
614 if (touched_regions->size() <= 2) {
615 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
616 add_crossfade (xfade);
623 /* [---- top ------------------------]
624 * { ==== bottom ============ }
627 if (_session.config.get_xfade_model() == FullCrossfade) {
629 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
630 if (touched_regions->size() <= 2) {
631 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
632 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
633 add_crossfade (xfade);
637 touched_regions = regions_touched (bottom->first_frame(),
638 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
640 if (touched_regions->size() <= 2) {
641 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
642 add_crossfade (xfade);
647 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
648 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
649 add_crossfade (xfade);
653 catch (failed_constructor& err) {
657 catch (Crossfade::NoCrossfadeHere& err) {
663 delete touched_regions;
667 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
669 Crossfades::iterator ci;
671 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
672 if (*(*ci) == *xfade) { // Crossfade::operator==()
677 if (ci != _crossfades.end()) {
678 // it will just go away
680 _crossfades.push_back (xfade);
682 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
683 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
685 notify_crossfade_added (xfade);
689 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
691 if (g_atomic_int_get(&block_notifications)) {
692 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
694 NewCrossfade (x); /* EMIT SIGNAL */
699 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
701 Crossfades::iterator i;
702 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
704 xfade->in()->resume_fade_in ();
705 xfade->out()->resume_fade_out ();
707 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
708 _crossfades.erase (i);
713 AudioPlaylist::set_state (const XMLNode& node, int version)
717 XMLNodeConstIterator niter;
721 if (Playlist::set_state (node, version)) {
727 nlist = node.children();
729 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
733 if (child->name() != "Crossfade") {
738 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
739 _crossfades.push_back (xfade);
740 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
741 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
745 catch (failed_constructor& err) {
746 // cout << string_compose (_("could not create crossfade object in playlist %1"),
760 AudioPlaylist::clear (bool with_signals)
762 _crossfades.clear ();
763 Playlist::clear (with_signals);
767 AudioPlaylist::state (bool full_state)
769 XMLNode& node = Playlist::state (full_state);
772 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
773 node.add_child_nocopy ((*i)->get_state());
781 AudioPlaylist::dump () const
783 boost::shared_ptr<Region>r;
784 boost::shared_ptr<Crossfade> x;
786 cerr << "Playlist \"" << _name << "\" " << endl
787 << regions.size() << " regions "
788 << _crossfades.size() << " crossfades"
791 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
793 cerr << " " << r->name() << " @ " << r << " ["
794 << r->start() << "+" << r->length()
802 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
813 << (x->active() ? "yes" : "no")
819 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
821 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
827 bool changed = false;
828 Crossfades::iterator c, ctmp;
829 set<boost::shared_ptr<Crossfade> > unique_xfades;
832 RegionLock rlock (this);
834 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
836 RegionList::iterator tmp = i;
839 if ((*i) == region) {
847 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
849 set<boost::shared_ptr<Region> >::iterator xtmp = x;
852 if ((*x) == region) {
853 all_regions.erase (x);
860 region->set_playlist (boost::shared_ptr<Playlist>());
863 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
867 if ((*c)->involves (r)) {
868 unique_xfades.insert (*c);
869 _crossfades.erase (c);
876 /* overload this, it normally means "removed", not destroyed */
877 notify_region_removed (region);
884 AudioPlaylist::crossfade_changed (const PropertyChange&)
886 if (in_flush || in_set_state) {
890 /* XXX is there a loop here? can an xfade change not happen
891 due to a playlist change? well, sure activation would
892 be an example. maybe we should check the type of change
896 notify_contents_changed ();
900 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
902 if (in_flush || in_set_state) {
906 PropertyChange our_interests;
908 our_interests.add (Properties::fade_in_active);
909 our_interests.add (Properties::fade_out_active);
910 our_interests.add (Properties::scale_amplitude);
911 our_interests.add (Properties::envelope_active);
912 our_interests.add (Properties::envelope);
913 our_interests.add (Properties::fade_in);
914 our_interests.add (Properties::fade_out);
916 bool parent_wants_notify;
918 parent_wants_notify = Playlist::region_changed (what_changed, region);
920 if (parent_wants_notify || (what_changed.contains (our_interests))) {
921 notify_contents_changed ();
928 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
930 RegionLock rlock (this);
932 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
933 framepos_t const start = (*i)->position ();
934 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
936 if (frame >= start && frame <= end) {
937 clist.push_back (*i);
943 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
945 RegionLock rl (this, false);
946 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
952 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
954 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
958 /* don't remove crossfades here; they will be dealt with by the dependency code */
961 boost::shared_ptr<Crossfade>
962 AudioPlaylist::find_crossfade (const PBD::ID& id) const
964 Crossfades::const_iterator i = _crossfades.begin ();
965 while (i != _crossfades.end() && (*i)->id() != id) {
969 if (i == _crossfades.end()) {
970 return boost::shared_ptr<Crossfade> ();
976 struct crossfade_triple {
977 boost::shared_ptr<Region> old_in;
978 boost::shared_ptr<Region> new_in;
979 boost::shared_ptr<Region> new_out;
983 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
985 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
991 /* our argument is a vector of old and new regions. Each old region
992 might be participant in a crossfade that is already present. Each new
993 region is a copy of the old region, present in the other playlist.
995 our task is to find all the relevant xfades in our playlist (involving
996 the "old" regions) and place copies of them in the other playlist.
999 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1000 CrossfadeInfo crossfade_info;
1002 /* build up a record that links crossfades, old regions and new regions
1005 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1007 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1009 if ((*i)->in() == on->first) {
1011 CrossfadeInfo::iterator cf;
1013 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1015 /* already have a record for the old fade-in region,
1016 so note the new fade-in region
1019 cf->second.new_in = on->second;
1023 /* add a record of this crossfade, keeping an association
1024 with the new fade-in region
1027 crossfade_triple ct;
1029 ct.old_in = on->first;
1030 ct.new_in = on->second;
1032 crossfade_info[*i] = ct;
1035 } else if ((*i)->out() == on->first) {
1037 /* this old region is the fade-out region of this crossfade */
1039 CrossfadeInfo::iterator cf;
1041 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1043 /* already have a record for this crossfade, so just keep
1044 an association for the new fade out region
1047 cf->second.new_out = on->second;
1051 /* add a record of this crossfade, keeping an association
1052 with the new fade-in region
1055 crossfade_triple ct;
1057 ct.old_in = on->first;
1058 ct.new_out = on->second;
1060 crossfade_info[*i] = ct;
1066 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1068 /* for each crossfade that involves at least two of the old regions,
1069 create a new identical crossfade with the new regions
1072 if (!ci->second.new_in || !ci->second.new_out) {
1076 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1077 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1078 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1080 /* add it at the right position - which must be at the start
1081 * of the fade-in region
1084 new_xfade->set_position (ci->second.new_in->position());
1085 other_audio->add_crossfade (new_xfade);
1090 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1092 RegionSortByPosition cmp;
1093 boost::shared_ptr<AudioRegion> ar;
1095 sort (copies.begin(), copies.end(), cmp);
1097 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1099 /* disable fade in of the first region */
1102 ar->set_fade_in_active (false);
1105 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1107 /* disable fade out of the last region */
1110 ar->set_fade_out_active (false);
1115 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1117 RegionSortByPosition cmp;
1118 boost::shared_ptr<AudioRegion> ar;
1119 boost::shared_ptr<AudioRegion> cr;
1121 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1125 sort (originals.begin(), originals.end(), cmp);
1127 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1129 /* copy the fade in of the first into the compound region */
1132 cr->set_fade_in (ar->fade_in());
1135 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1138 /* copy the fade out of the last into the compound region */
1139 cr->set_fade_out (ar->fade_out());
1144 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1146 RegionSortByPosition cmp;
1147 boost::shared_ptr<AudioRegion> ar;
1148 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1154 sort (originals.begin(), originals.end(), cmp);
1156 /* no need to call clear_changes() on the originals because that is
1157 * done within Playlist::uncombine ()
1160 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1162 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1166 /* scale the uncombined regions by any gain setting for the
1170 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1172 if (i == originals.begin()) {
1174 /* copy the compound region's fade in back into the first
1178 if (cr->fade_in()->back()->when <= ar->length()) {
1179 /* don't do this if the fade is longer than the
1182 ar->set_fade_in (cr->fade_in());
1186 } else if (*i == originals.back()) {
1188 /* copy the compound region's fade out back into the last
1192 if (cr->fade_out()->back()->when <= ar->length()) {
1193 /* don't do this if the fade is longer than the
1196 ar->set_fade_out (cr->fade_out());
1201 _session.add_command (new StatefulDiffCommand (*i));