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();
120 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
121 : Playlist (session, name, DataType::AUDIO, hidden)
122 , _crossfades (*this)
124 add_property (_crossfades);
127 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
128 : Playlist (other, name, hidden)
129 , _crossfades (*this)
131 add_property (_crossfades);
133 RegionList::const_iterator in_o = other->regions.begin();
134 RegionList::iterator in_n = regions.begin();
136 while (in_o != other->regions.end()) {
137 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
139 // We look only for crossfades which begin with the current region, so we don't get doubles
140 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
141 if ((*xfades)->in() == ar) {
142 // We found one! Now copy it!
144 RegionList::const_iterator out_o = other->regions.begin();
145 RegionList::const_iterator out_n = regions.begin();
147 while (out_o != other->regions.end()) {
149 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
151 if ((*xfades)->out() == ar2) {
152 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
153 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
154 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
155 add_crossfade(new_fade);
162 // cerr << "HUH!? second region in the crossfade not found!" << endl;
171 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
172 : Playlist (other, start, cnt, name, hidden)
173 , _crossfades (*this)
175 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
178 add_property (_crossfades);
180 framepos_t const end = start + cnt - 1;
182 /* Audio regions that have been created by the Playlist constructor
183 will currently have the same fade in/out as the regions that they
184 were created from. This is wrong, so reset the fades here.
187 RegionList::iterator ours = regions.begin ();
189 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
190 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
193 framecnt_t fade_in = 64;
194 framecnt_t fade_out = 64;
196 switch (region->coverage (start, end)) {
200 case OverlapInternal:
202 framecnt_t const offset = start - region->position ();
203 framecnt_t const trim = region->last_frame() - end;
204 if (region->fade_in()->back()->when > offset) {
205 fade_in = region->fade_in()->back()->when - offset;
207 if (region->fade_out()->back()->when > trim) {
208 fade_out = region->fade_out()->back()->when - trim;
214 if (end > region->position() + region->fade_in()->back()->when)
215 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
216 if (end > region->last_frame() - region->fade_out()->back()->when)
217 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
222 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
223 fade_out = region->fade_out()->back()->when;
225 if (start < region->position() + region->fade_in()->back()->when)
226 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
230 case OverlapExternal:
231 fade_in = region->fade_in()->back()->when;
232 fade_out = region->fade_out()->back()->when;
236 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
239 our_region->set_fade_in_length (fade_in);
240 our_region->set_fade_out_length (fade_out);
246 /* this constructor does NOT notify others (session) */
249 AudioPlaylist::~AudioPlaylist ()
251 _crossfades.clear ();
254 struct RegionSortByLayer {
255 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
256 return a->layer() < b->layer();
261 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
262 framecnt_t cnt, unsigned chan_n)
264 framecnt_t ret = cnt;
266 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n",
267 name(), start, cnt, chan_n, regions.size(), _crossfades.size()));
269 /* optimizing this memset() away involves a lot of conditionals
270 that may well cause more of a hit due to cache misses
271 and related stuff than just doing this here.
273 it would be great if someone could measure this
276 one way or another, parts of the requested area
277 that are not written to by Region::region_at()
278 for all Regions that cover the area need to be
282 memset (buf, 0, sizeof (Sample) * cnt);
284 /* this function is never called from a realtime thread, so
285 its OK to block (for short intervals).
288 Glib::RecMutex::Lock rm (region_lock);
290 framepos_t const end = start + cnt - 1;
292 boost::shared_ptr<RegionList> rlist = regions_to_read (start, start+cnt);
294 if (rlist->empty()) {
298 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
299 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
300 vector<uint32_t> relevant_layers;
302 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
303 if ((*i)->coverage (start, end) != OverlapNone) {
304 relevant_regions[(*i)->layer()].push_back (*i);
305 relevant_layers.push_back ((*i)->layer());
309 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
311 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
312 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n",
313 name(), (*i)->out()->name(), (*i)->in()->name(),
314 (*i)->first_frame(), (*i)->last_frame(),
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 (coverage = %2), place on layer %1\n",
319 (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end))));
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);
349 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
350 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
351 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
360 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
362 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
369 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
374 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
376 if ((*i)->involves (r)) {
377 i = _crossfades.erase (i);
386 AudioPlaylist::flush_notifications (bool from_undo)
388 Playlist::flush_notifications (from_undo);
396 Crossfades::iterator a;
397 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
398 NewCrossfade (*a); /* EMIT SIGNAL */
401 _pending_xfade_adds.clear ();
407 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
409 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
410 set<boost::shared_ptr<Crossfade> > updated;
416 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
418 Crossfades::iterator tmp;
423 /* only update them once */
425 if ((*x)->involves (ar)) {
427 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
430 /* x was successfully inserted into the set, so it has not already been updated */
435 catch (Crossfade::NoCrossfadeHere& err) {
436 // relax, Invalidated during refresh
446 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
448 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
449 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
450 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
452 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
453 Crossfades::iterator tmp;
457 boost::shared_ptr<Crossfade> fade;
459 if ((*x)->_in == orig) {
460 if (! (*x)->covers(right->position())) {
461 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
463 // Overlap, the crossfade is copied on the left side of the right region instead
464 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
468 if ((*x)->_out == orig) {
469 if (! (*x)->covers(right->position())) {
470 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
472 // Overlap, the crossfade is copied on the right side of the left region instead
473 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
478 _crossfades.remove (*x);
479 add_crossfade (fade);
486 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
488 boost::shared_ptr<AudioRegion> other;
489 boost::shared_ptr<AudioRegion> region;
490 boost::shared_ptr<AudioRegion> top;
491 boost::shared_ptr<AudioRegion> bottom;
492 boost::shared_ptr<Crossfade> xfade;
493 boost::shared_ptr<RegionList> touched_regions;
495 if (in_set_state || in_partition) {
499 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
500 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
506 refresh_dependents (r);
510 if (!_session.config.get_auto_xfade()) {
514 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
515 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
517 if (other == region) {
521 if (other->muted() || region->muted()) {
525 if (other->position() == r->position() && other->length() == r->length()) {
526 /* precise overlay of two regions - no xfade */
530 if (other->layer() < region->layer()) {
538 if (!top->opaque()) {
542 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
544 touched_regions.reset ();
547 framecnt_t xfade_length;
552 case OverlapInternal:
553 /* {=============== top =============}
554 * [ ----- bottom ------- ]
558 case OverlapExternal:
560 /* [ -------- top ------- ]
561 * {=========== bottom =============}
564 /* to avoid discontinuities at the region boundaries of an internal
565 overlap (this region is completely within another), we create
566 two hidden crossfades at each boundary. this is not dependent
567 on the auto-xfade option, because we require it as basic
571 xfade_length = min ((framecnt_t) 720, top->length());
573 if (top_region_at (top->first_frame()) == top) {
575 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
576 xfade->set_position (top->first_frame());
577 add_crossfade (xfade);
580 if (top_region_at (top->last_frame() - 1) == top) {
583 only add a fade out if there is no region on top of the end of 'top' (which
587 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, EndOfOut));
588 xfade->set_position (top->last_frame() - xfade_length);
589 add_crossfade (xfade);
594 /* { ==== top ============ }
595 * [---- bottom -------------------]
598 if (_session.config.get_xfade_model() == FullCrossfade) {
599 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
600 if (touched_regions->size() <= 2) {
601 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
602 add_crossfade (xfade);
606 touched_regions = regions_touched (top->first_frame(),
607 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
609 if (touched_regions->size() <= 2) {
610 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
611 add_crossfade (xfade);
618 /* [---- top ------------------------]
619 * { ==== bottom ============ }
622 if (_session.config.get_xfade_model() == FullCrossfade) {
624 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
625 if (touched_regions->size() <= 2) {
626 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
627 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
628 add_crossfade (xfade);
632 touched_regions = regions_touched (bottom->first_frame(),
633 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
635 if (touched_regions->size() <= 2) {
636 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
637 add_crossfade (xfade);
642 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
643 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
644 add_crossfade (xfade);
648 catch (failed_constructor& err) {
652 catch (Crossfade::NoCrossfadeHere& err) {
660 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
662 Crossfades::iterator ci;
664 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
665 if (*(*ci) == *xfade) { // Crossfade::operator==()
670 if (ci != _crossfades.end()) {
671 // it will just go away
673 _crossfades.push_back (xfade);
675 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
676 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
678 notify_crossfade_added (xfade);
682 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
684 if (g_atomic_int_get(&block_notifications)) {
685 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
687 NewCrossfade (x); /* EMIT SIGNAL */
692 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
694 Crossfades::iterator i;
695 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
697 xfade->in()->resume_fade_in ();
698 xfade->out()->resume_fade_out ();
700 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
701 _crossfades.erase (i);
706 AudioPlaylist::set_state (const XMLNode& node, int version)
710 XMLNodeConstIterator niter;
714 if (Playlist::set_state (node, version)) {
720 nlist = node.children();
722 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
726 if (child->name() != "Crossfade") {
731 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
732 _crossfades.push_back (xfade);
733 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
734 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
738 catch (failed_constructor& err) {
739 // cout << string_compose (_("could not create crossfade object in playlist %1"),
753 AudioPlaylist::clear (bool with_signals)
755 _crossfades.clear ();
756 Playlist::clear (with_signals);
760 AudioPlaylist::state (bool full_state)
762 XMLNode& node = Playlist::state (full_state);
765 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
766 node.add_child_nocopy ((*i)->get_state());
774 AudioPlaylist::dump () const
776 boost::shared_ptr<Region>r;
777 boost::shared_ptr<Crossfade> x;
779 cerr << "Playlist \"" << _name << "\" " << endl
780 << regions.size() << " regions "
781 << _crossfades.size() << " crossfades"
784 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
786 cerr << " " << r->name() << " @ " << r << " ["
787 << r->start() << "+" << r->length()
795 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
806 << (x->active() ? "yes" : "no")
812 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
814 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
820 bool changed = false;
821 Crossfades::iterator c, ctmp;
822 set<boost::shared_ptr<Crossfade> > unique_xfades;
825 RegionLock rlock (this);
827 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
829 RegionList::iterator tmp = i;
832 if ((*i) == region) {
840 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
842 set<boost::shared_ptr<Region> >::iterator xtmp = x;
845 if ((*x) == region) {
846 all_regions.erase (x);
853 region->set_playlist (boost::shared_ptr<Playlist>());
856 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
860 if ((*c)->involves (r)) {
861 unique_xfades.insert (*c);
862 _crossfades.erase (c);
869 /* overload this, it normally means "removed", not destroyed */
870 notify_region_removed (region);
877 AudioPlaylist::crossfade_changed (const PropertyChange&)
879 if (in_flush || in_set_state) {
883 /* XXX is there a loop here? can an xfade change not happen
884 due to a playlist change? well, sure activation would
885 be an example. maybe we should check the type of change
889 notify_contents_changed ();
893 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
895 if (in_flush || in_set_state) {
899 PropertyChange our_interests;
901 our_interests.add (Properties::fade_in_active);
902 our_interests.add (Properties::fade_out_active);
903 our_interests.add (Properties::scale_amplitude);
904 our_interests.add (Properties::envelope_active);
905 our_interests.add (Properties::envelope);
906 our_interests.add (Properties::fade_in);
907 our_interests.add (Properties::fade_out);
909 bool parent_wants_notify;
911 parent_wants_notify = Playlist::region_changed (what_changed, region);
913 if (parent_wants_notify || (what_changed.contains (our_interests))) {
914 notify_contents_changed ();
921 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
923 RegionLock rlock (this);
925 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
926 framepos_t const start = (*i)->position ();
927 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
929 if (frame >= start && frame <= end) {
930 clist.push_back (*i);
936 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
938 RegionLock rl (this, false);
939 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
945 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
947 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
951 /* don't remove crossfades here; they will be dealt with by the dependency code */
954 boost::shared_ptr<Crossfade>
955 AudioPlaylist::find_crossfade (const PBD::ID& id) const
957 Crossfades::const_iterator i = _crossfades.begin ();
958 while (i != _crossfades.end() && (*i)->id() != id) {
962 if (i == _crossfades.end()) {
963 return boost::shared_ptr<Crossfade> ();
969 struct crossfade_triple {
970 boost::shared_ptr<Region> old_in;
971 boost::shared_ptr<Region> new_in;
972 boost::shared_ptr<Region> new_out;
976 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
978 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
984 /* our argument is a vector of old and new regions. Each old region
985 might be participant in a crossfade that is already present. Each new
986 region is a copy of the old region, present in the other playlist.
988 our task is to find all the relevant xfades in our playlist (involving
989 the "old" regions) and place copies of them in the other playlist.
992 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
993 CrossfadeInfo crossfade_info;
995 /* build up a record that links crossfades, old regions and new regions
998 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1000 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1002 if ((*i)->in() == on->first) {
1004 CrossfadeInfo::iterator cf;
1006 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1008 /* already have a record for the old fade-in region,
1009 so note the new fade-in region
1012 cf->second.new_in = on->second;
1016 /* add a record of this crossfade, keeping an association
1017 with the new fade-in region
1020 crossfade_triple ct;
1022 ct.old_in = on->first;
1023 ct.new_in = on->second;
1025 crossfade_info[*i] = ct;
1028 } else if ((*i)->out() == on->first) {
1030 /* this old region is the fade-out region of this crossfade */
1032 CrossfadeInfo::iterator cf;
1034 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1036 /* already have a record for this crossfade, so just keep
1037 an association for the new fade out region
1040 cf->second.new_out = on->second;
1044 /* add a record of this crossfade, keeping an association
1045 with the new fade-in region
1048 crossfade_triple ct;
1050 ct.old_in = on->first;
1051 ct.new_out = on->second;
1053 crossfade_info[*i] = ct;
1059 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1061 /* for each crossfade that involves at least two of the old regions,
1062 create a new identical crossfade with the new regions
1065 if (!ci->second.new_in || !ci->second.new_out) {
1069 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1070 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1071 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1073 /* add it at the right position - which must be at the start
1074 * of the fade-in region
1077 new_xfade->set_position (ci->second.new_in->position());
1078 other_audio->add_crossfade (new_xfade);
1083 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1085 RegionSortByPosition cmp;
1086 boost::shared_ptr<AudioRegion> ar;
1088 sort (copies.begin(), copies.end(), cmp);
1090 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1092 /* disable fade in of the first region */
1095 ar->set_fade_in_active (false);
1098 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1100 /* disable fade out of the last region */
1103 ar->set_fade_out_active (false);
1108 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1110 RegionSortByPosition cmp;
1111 boost::shared_ptr<AudioRegion> ar;
1112 boost::shared_ptr<AudioRegion> cr;
1114 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1118 sort (originals.begin(), originals.end(), cmp);
1120 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1122 /* copy the fade in of the first into the compound region */
1125 cr->set_fade_in (ar->fade_in());
1128 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1131 /* copy the fade out of the last into the compound region */
1132 cr->set_fade_out (ar->fade_out());
1137 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1139 RegionSortByPosition cmp;
1140 boost::shared_ptr<AudioRegion> ar;
1141 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1147 sort (originals.begin(), originals.end(), cmp);
1149 /* no need to call clear_changes() on the originals because that is
1150 * done within Playlist::uncombine ()
1153 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1155 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1159 /* scale the uncombined regions by any gain setting for the
1163 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1165 if (i == originals.begin()) {
1167 /* copy the compound region's fade in back into the first
1171 if (cr->fade_in()->back()->when <= ar->length()) {
1172 /* don't do this if the fade is longer than the
1175 ar->set_fade_in (cr->fade_in());
1179 } else if (*i == originals.back()) {
1181 /* copy the compound region's fade out back into the last
1185 if (cr->fade_out()->back()->when <= ar->length()) {
1186 /* don't do this if the fade is longer than the
1189 ar->set_fade_out (cr->fade_out());
1194 _session.add_command (new StatefulDiffCommand (*i));