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 RegionList* rlist = regions_to_read (start, start+cnt);
294 if (rlist->empty()) {
299 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
300 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
301 vector<uint32_t> relevant_layers;
303 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
304 if ((*i)->coverage (start, end) != OverlapNone) {
305 relevant_regions[(*i)->layer()].push_back (*i);
306 relevant_layers.push_back ((*i)->layer());
310 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
312 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
313 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ... [ %4 ... %5 | %6 ... %7]\n",
314 name(), (*i)->out()->name(), (*i)->in()->name(),
315 (*i)->first_frame(), (*i)->last_frame(),
317 if ((*i)->coverage (start, end) != OverlapNone) {
318 relevant_xfades[(*i)->upper_layer()].push_back (*i);
319 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant (coverage = %2), place on layer %1\n",
320 (*i)->upper_layer(), enum_2_string ((*i)->coverage (start, end))));
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 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
339 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
340 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
343 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
344 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
345 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
347 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
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);
362 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
364 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
371 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
376 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
378 if ((*i)->involves (r)) {
379 i = _crossfades.erase (i);
388 AudioPlaylist::flush_notifications (bool from_undo)
390 Playlist::flush_notifications (from_undo);
398 Crossfades::iterator a;
399 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
400 NewCrossfade (*a); /* EMIT SIGNAL */
403 _pending_xfade_adds.clear ();
409 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
411 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
412 set<boost::shared_ptr<Crossfade> > updated;
418 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
420 Crossfades::iterator tmp;
425 /* only update them once */
427 if ((*x)->involves (ar)) {
429 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
432 /* x was successfully inserted into the set, so it has not already been updated */
437 catch (Crossfade::NoCrossfadeHere& err) {
438 // relax, Invalidated during refresh
448 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
450 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
451 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
452 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
454 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
455 Crossfades::iterator tmp;
459 boost::shared_ptr<Crossfade> fade;
461 if ((*x)->_in == orig) {
462 if (! (*x)->covers(right->position())) {
463 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
465 // Overlap, the crossfade is copied on the left side of the right region instead
466 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
470 if ((*x)->_out == orig) {
471 if (! (*x)->covers(right->position())) {
472 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
474 // Overlap, the crossfade is copied on the right side of the left region instead
475 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
480 _crossfades.remove (*x);
481 add_crossfade (fade);
488 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
490 boost::shared_ptr<AudioRegion> other;
491 boost::shared_ptr<AudioRegion> region;
492 boost::shared_ptr<AudioRegion> top;
493 boost::shared_ptr<AudioRegion> bottom;
494 boost::shared_ptr<Crossfade> xfade;
495 RegionList* touched_regions = 0;
497 if (in_set_state || in_partition) {
501 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
502 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
508 refresh_dependents (r);
512 if (!_session.config.get_auto_xfade()) {
516 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
517 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
519 if (other == region) {
523 if (other->muted() || region->muted()) {
527 if (other->position() == r->position() && other->length() == r->length()) {
528 /* precise overlay of two regions - no xfade */
532 if (other->layer() < region->layer()) {
540 if (!top->opaque()) {
544 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
546 delete touched_regions;
550 framecnt_t xfade_length;
555 case OverlapInternal:
556 /* {=============== top =============}
557 * [ ----- bottom ------- ]
561 case OverlapExternal:
563 /* [ -------- top ------- ]
564 * {=========== bottom =============}
567 /* to avoid discontinuities at the region boundaries of an internal
568 overlap (this region is completely within another), we create
569 two hidden crossfades at each boundary. this is not dependent
570 on the auto-xfade option, because we require it as basic
574 xfade_length = min ((framecnt_t) 720, top->length());
576 if (top_region_at (top->first_frame()) == top) {
578 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, StartOfIn));
579 xfade->set_position (top->first_frame());
580 add_crossfade (xfade);
583 if (top_region_at (top->last_frame() - 1) == top) {
586 only add a fade out if there is no region on top of the end of 'top' (which
590 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, EndOfOut));
591 xfade->set_position (top->last_frame() - xfade_length);
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 if (Playlist::set_state (node, version)) {
725 nlist = node.children();
727 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
731 if (child->name() != "Crossfade") {
736 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
737 _crossfades.push_back (xfade);
738 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
739 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
743 catch (failed_constructor& err) {
744 // cout << string_compose (_("could not create crossfade object in playlist %1"),
758 AudioPlaylist::clear (bool with_signals)
760 _crossfades.clear ();
761 Playlist::clear (with_signals);
765 AudioPlaylist::state (bool full_state)
767 XMLNode& node = Playlist::state (full_state);
770 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
771 node.add_child_nocopy ((*i)->get_state());
779 AudioPlaylist::dump () const
781 boost::shared_ptr<Region>r;
782 boost::shared_ptr<Crossfade> x;
784 cerr << "Playlist \"" << _name << "\" " << endl
785 << regions.size() << " regions "
786 << _crossfades.size() << " crossfades"
789 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
791 cerr << " " << r->name() << " @ " << r << " ["
792 << r->start() << "+" << r->length()
800 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
811 << (x->active() ? "yes" : "no")
817 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
819 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
825 bool changed = false;
826 Crossfades::iterator c, ctmp;
827 set<boost::shared_ptr<Crossfade> > unique_xfades;
830 RegionLock rlock (this);
832 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
834 RegionList::iterator tmp = i;
837 if ((*i) == region) {
845 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
847 set<boost::shared_ptr<Region> >::iterator xtmp = x;
850 if ((*x) == region) {
851 all_regions.erase (x);
858 region->set_playlist (boost::shared_ptr<Playlist>());
861 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
865 if ((*c)->involves (r)) {
866 unique_xfades.insert (*c);
867 _crossfades.erase (c);
874 /* overload this, it normally means "removed", not destroyed */
875 notify_region_removed (region);
882 AudioPlaylist::crossfade_changed (const PropertyChange&)
884 if (in_flush || in_set_state) {
888 /* XXX is there a loop here? can an xfade change not happen
889 due to a playlist change? well, sure activation would
890 be an example. maybe we should check the type of change
894 notify_contents_changed ();
898 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
900 if (in_flush || in_set_state) {
904 PropertyChange our_interests;
906 our_interests.add (Properties::fade_in_active);
907 our_interests.add (Properties::fade_out_active);
908 our_interests.add (Properties::scale_amplitude);
909 our_interests.add (Properties::envelope_active);
910 our_interests.add (Properties::envelope);
911 our_interests.add (Properties::fade_in);
912 our_interests.add (Properties::fade_out);
914 bool parent_wants_notify;
916 parent_wants_notify = Playlist::region_changed (what_changed, region);
918 if (parent_wants_notify || (what_changed.contains (our_interests))) {
919 notify_contents_changed ();
926 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
928 RegionLock rlock (this);
930 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
931 framepos_t const start = (*i)->position ();
932 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
934 if (frame >= start && frame <= end) {
935 clist.push_back (*i);
941 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
943 RegionLock rl (this, false);
944 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
950 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
952 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
956 /* don't remove crossfades here; they will be dealt with by the dependency code */
959 boost::shared_ptr<Crossfade>
960 AudioPlaylist::find_crossfade (const PBD::ID& id) const
962 Crossfades::const_iterator i = _crossfades.begin ();
963 while (i != _crossfades.end() && (*i)->id() != id) {
967 if (i == _crossfades.end()) {
968 return boost::shared_ptr<Crossfade> ();
974 struct crossfade_triple {
975 boost::shared_ptr<Region> old_in;
976 boost::shared_ptr<Region> new_in;
977 boost::shared_ptr<Region> new_out;
981 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
983 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
989 /* our argument is a vector of old and new regions. Each old region
990 might be participant in a crossfade that is already present. Each new
991 region is a copy of the old region, present in the other playlist.
993 our task is to find all the relevant xfades in our playlist (involving
994 the "old" regions) and place copies of them in the other playlist.
997 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
998 CrossfadeInfo crossfade_info;
1000 /* build up a record that links crossfades, old regions and new regions
1003 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1005 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1007 if ((*i)->in() == on->first) {
1009 CrossfadeInfo::iterator cf;
1011 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1013 /* already have a record for the old fade-in region,
1014 so note the new fade-in region
1017 cf->second.new_in = on->second;
1021 /* add a record of this crossfade, keeping an association
1022 with the new fade-in region
1025 crossfade_triple ct;
1027 ct.old_in = on->first;
1028 ct.new_in = on->second;
1030 crossfade_info[*i] = ct;
1033 } else if ((*i)->out() == on->first) {
1035 /* this old region is the fade-out region of this crossfade */
1037 CrossfadeInfo::iterator cf;
1039 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1041 /* already have a record for this crossfade, so just keep
1042 an association for the new fade out region
1045 cf->second.new_out = on->second;
1049 /* add a record of this crossfade, keeping an association
1050 with the new fade-in region
1053 crossfade_triple ct;
1055 ct.old_in = on->first;
1056 ct.new_out = on->second;
1058 crossfade_info[*i] = ct;
1064 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1066 /* for each crossfade that involves at least two of the old regions,
1067 create a new identical crossfade with the new regions
1070 if (!ci->second.new_in || !ci->second.new_out) {
1074 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1075 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1076 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1078 /* add it at the right position - which must be at the start
1079 * of the fade-in region
1082 new_xfade->set_position (ci->second.new_in->position());
1083 other_audio->add_crossfade (new_xfade);
1088 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1090 RegionSortByPosition cmp;
1091 boost::shared_ptr<AudioRegion> ar;
1093 sort (copies.begin(), copies.end(), cmp);
1095 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1097 /* disable fade in of the first region */
1100 ar->set_fade_in_active (false);
1103 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1105 /* disable fade out of the last region */
1108 ar->set_fade_out_active (false);
1113 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1115 RegionSortByPosition cmp;
1116 boost::shared_ptr<AudioRegion> ar;
1117 boost::shared_ptr<AudioRegion> cr;
1119 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1123 sort (originals.begin(), originals.end(), cmp);
1125 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1127 /* copy the fade in of the first into the compound region */
1130 cr->set_fade_in (ar->fade_in());
1133 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1136 /* copy the fade out of the last into the compound region */
1137 cr->set_fade_out (ar->fade_out());
1142 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1144 RegionSortByPosition cmp;
1145 boost::shared_ptr<AudioRegion> ar;
1146 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1152 sort (originals.begin(), originals.end(), cmp);
1154 /* no need to call clear_changes() on the originals because that is
1155 * done within Playlist::uncombine ()
1158 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1160 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1164 /* scale the uncombined regions by any gain setting for the
1168 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1170 if (i == originals.begin()) {
1172 /* copy the compound region's fade in back into the first
1176 if (cr->fade_in()->back()->when <= ar->length()) {
1177 /* don't do this if the fade is longer than the
1180 ar->set_fade_in (cr->fade_in());
1184 } else if (*i == originals.back()) {
1186 /* copy the compound region's fade out back into the last
1190 if (cr->fade_out()->back()->when <= ar->length()) {
1191 /* don't do this if the fade is longer than the
1194 ar->set_fade_out (cr->fade_out());
1199 _session.add_command (new StatefulDiffCommand (*i));