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;
290 RegionList* rlist = regions_to_read (start, start+cnt);
292 if (rlist->empty()) {
297 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
298 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
299 vector<uint32_t> relevant_layers;
301 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
302 if ((*i)->coverage (start, end) != OverlapNone) {
303 relevant_regions[(*i)->layer()].push_back (*i);
304 relevant_layers.push_back ((*i)->layer());
308 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
310 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
311 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n",
312 name(), (*i)->out()->name(), (*i)->in()->name(),
313 (*i)->first_frame(), (*i)->last_frame(),
315 if ((*i)->coverage (start, end) != OverlapNone) {
316 relevant_xfades[(*i)->upper_layer()].push_back (*i);
317 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n",
318 (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end))));
322 // RegionSortByLayer layer_cmp;
323 // relevant_regions.sort (layer_cmp);
325 /* XXX this whole per-layer approach is a hack that
326 should be removed once Crossfades become
327 CrossfadeRegions and we just grab a list of relevant
328 regions and call read_at() on all of them.
331 sort (relevant_layers.begin(), relevant_layers.end());
333 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
335 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *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);
348 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
349 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
350 (*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 RegionList* touched_regions = 0;
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 delete touched_regions;
548 framecnt_t xfade_length;
553 case OverlapInternal:
554 /* {=============== top =============}
555 * [ ----- bottom ------- ]
559 case OverlapExternal:
561 /* [ -------- top ------- ]
562 * {=========== bottom =============}
565 /* to avoid discontinuities at the region boundaries of an internal
566 overlap (this region is completely within another), we create
567 two hidden crossfades at each boundary. this is not dependent
568 on the auto-xfade option, because we require it as basic
572 xfade_length = min ((framecnt_t) 720, top->length());
574 if (top_region_at (top->first_frame()) == top) {
576 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
577 xfade->set_position (top->first_frame());
578 add_crossfade (xfade);
581 if (top_region_at (top->last_frame() - 1) == top) {
584 only add a fade out if there is no region on top of the end of 'top' (which
588 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, EndOfOut));
589 xfade->set_position (top->last_frame() - xfade_length);
590 add_crossfade (xfade);
595 /* { ==== top ============ }
596 * [---- bottom -------------------]
599 if (_session.config.get_xfade_model() == FullCrossfade) {
600 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
601 if (touched_regions->size() <= 2) {
602 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
603 add_crossfade (xfade);
607 touched_regions = regions_touched (top->first_frame(),
608 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
610 if (touched_regions->size() <= 2) {
611 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
612 add_crossfade (xfade);
619 /* [---- top ------------------------]
620 * { ==== bottom ============ }
623 if (_session.config.get_xfade_model() == FullCrossfade) {
625 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
626 if (touched_regions->size() <= 2) {
627 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
628 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
629 add_crossfade (xfade);
633 touched_regions = regions_touched (bottom->first_frame(),
634 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
636 if (touched_regions->size() <= 2) {
637 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
638 add_crossfade (xfade);
643 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
644 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
645 add_crossfade (xfade);
649 catch (failed_constructor& err) {
653 catch (Crossfade::NoCrossfadeHere& err) {
659 delete touched_regions;
663 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
665 Crossfades::iterator ci;
667 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
668 if (*(*ci) == *xfade) { // Crossfade::operator==()
673 if (ci != _crossfades.end()) {
674 // it will just go away
676 _crossfades.push_back (xfade);
678 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
679 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
681 notify_crossfade_added (xfade);
685 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
687 if (g_atomic_int_get(&block_notifications)) {
688 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
690 NewCrossfade (x); /* EMIT SIGNAL */
695 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
697 Crossfades::iterator i;
698 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
700 xfade->in()->resume_fade_in ();
701 xfade->out()->resume_fade_out ();
703 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
704 _crossfades.erase (i);
709 AudioPlaylist::set_state (const XMLNode& node, int version)
713 XMLNodeConstIterator niter;
717 if (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, Playlist* other) const
981 AudioPlaylist* other_audio = dynamic_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::const_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());
1081 other_audio->add_crossfade (new_xfade);
1086 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1088 RegionSortByPosition cmp;
1089 boost::shared_ptr<AudioRegion> ar;
1091 sort (copies.begin(), copies.end(), cmp);
1093 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1095 /* disable fade in of the first region */
1098 ar->set_fade_in_active (false);
1101 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1103 /* disable fade out of the last region */
1106 ar->set_fade_out_active (false);
1111 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1113 RegionSortByPosition cmp;
1114 boost::shared_ptr<AudioRegion> ar;
1115 boost::shared_ptr<AudioRegion> cr;
1117 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1121 sort (originals.begin(), originals.end(), cmp);
1123 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1125 /* copy the fade in of the first into the compound region */
1128 cr->set_fade_in (ar->fade_in());
1131 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1134 /* copy the fade out of the last into the compound region */
1135 cr->set_fade_out (ar->fade_out());
1140 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1142 RegionSortByPosition cmp;
1143 boost::shared_ptr<AudioRegion> ar;
1144 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1150 sort (originals.begin(), originals.end(), cmp);
1152 /* no need to call clear_changes() on the originals because that is
1153 * done within Playlist::uncombine ()
1156 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1158 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1162 /* scale the uncombined regions by any gain setting for the
1166 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1168 if (i == originals.begin()) {
1170 /* copy the compound region's fade in back into the first
1174 if (cr->fade_in()->back()->when <= ar->length()) {
1175 /* don't do this if the fade is longer than the
1178 ar->set_fade_in (cr->fade_in());
1182 } else if (*i == originals.back()) {
1184 /* copy the compound region's fade out back into the last
1188 if (cr->fade_out()->back()->when <= ar->length()) {
1189 /* don't do this if the fade is longer than the
1192 ar->set_fade_out (cr->fade_out());
1197 _session.add_command (new StatefulDiffCommand (*i));