2 Copyright (C) 2003 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "ardour/types.h"
25 #include "ardour/debug.h"
26 #include "ardour/configuration.h"
27 #include "ardour/audioplaylist.h"
28 #include "ardour/audioregion.h"
29 #include "ardour/crossfade.h"
30 #include "ardour/region_sorters.h"
31 #include "ardour/session.h"
32 #include "pbd/enumwriter.h"
36 using namespace ARDOUR;
41 namespace Properties {
42 PBD::PropertyDescriptor<bool> crossfades;
47 AudioPlaylist::make_property_quarks ()
49 Properties::crossfades.property_id = g_quark_from_static_string (X_("crossfades"));
50 DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for crossfades = %1\n", Properties::crossfades.property_id));
53 CrossfadeListProperty::CrossfadeListProperty (AudioPlaylist& pl)
54 : SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (Properties::crossfades.property_id, boost::bind (&AudioPlaylist::update, &pl, _1))
60 CrossfadeListProperty::CrossfadeListProperty (CrossfadeListProperty const & p)
61 : PBD::SequenceProperty<std::list<boost::shared_ptr<Crossfade> > > (p)
62 , _playlist (p._playlist)
68 CrossfadeListProperty *
69 CrossfadeListProperty::create () const
71 return new CrossfadeListProperty (_playlist);
74 CrossfadeListProperty *
75 CrossfadeListProperty::clone () const
77 return new CrossfadeListProperty (*this);
81 CrossfadeListProperty::get_content_as_xml (boost::shared_ptr<Crossfade> xfade, XMLNode & node) const
83 /* Crossfades are not written to any state when they are no
84 longer in use, so we must write their state here.
87 XMLNode& c = xfade->get_state ();
88 node.add_child_nocopy (c);
91 boost::shared_ptr<Crossfade>
92 CrossfadeListProperty::get_content_from_xml (XMLNode const & node) const
94 XMLNodeList const c = node.children ();
95 assert (c.size() == 1);
96 return boost::shared_ptr<Crossfade> (new Crossfade (_playlist, *c.front()));
100 AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
101 : Playlist (session, node, DataType::AUDIO, hidden)
102 , _crossfades (*this)
105 const XMLProperty* prop = node.property("type");
106 assert(!prop || DataType(prop->value()) == DataType::AUDIO);
109 add_property (_crossfades);
112 set_state (node, Stateful::loading_state_version);
116 AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
117 : Playlist (session, name, DataType::AUDIO, hidden)
118 , _crossfades (*this)
120 add_property (_crossfades);
123 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
124 : Playlist (other, name, hidden)
125 , _crossfades (*this)
127 add_property (_crossfades);
129 RegionList::const_iterator in_o = other->regions.begin();
130 RegionList::iterator in_n = regions.begin();
132 while (in_o != other->regions.end()) {
133 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
135 // We look only for crossfades which begin with the current region, so we don't get doubles
136 for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
137 if ((*xfades)->in() == ar) {
138 // We found one! Now copy it!
140 RegionList::const_iterator out_o = other->regions.begin();
141 RegionList::const_iterator out_n = regions.begin();
143 while (out_o != other->regions.end()) {
145 boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
147 if ((*xfades)->out() == ar2) {
148 boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
149 boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
150 boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
151 add_crossfade(new_fade);
158 // cerr << "HUH!? second region in the crossfade not found!" << endl;
167 AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, framepos_t start, framecnt_t cnt, string name, bool hidden)
168 : Playlist (other, start, cnt, name, hidden)
169 , _crossfades (*this)
171 RegionLock rlock2 (const_cast<AudioPlaylist*> (other.get()));
174 add_property (_crossfades);
176 framepos_t const end = start + cnt - 1;
178 /* Audio regions that have been created by the Playlist constructor
179 will currently have the same fade in/out as the regions that they
180 were created from. This is wrong, so reset the fades here.
183 RegionList::iterator ours = regions.begin ();
185 for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
186 boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (*i);
189 framecnt_t fade_in = 64;
190 framecnt_t fade_out = 64;
193 frameoffset_t offset;
195 switch (region->coverage (start, end)) {
199 case OverlapInternal:
201 framecnt_t const offset = start - region->position ();
202 framecnt_t const trim = region->last_frame() - end;
203 if (region->fade_in()->back()->when > offset) {
204 fade_in = region->fade_in()->back()->when - offset;
206 if (region->fade_out()->back()->when > trim) {
207 fade_out = region->fade_out()->back()->when - trim;
213 position = region->position() - start;
214 len = end - region->position();
216 if (end > region->position() + region->fade_in()->back()->when)
217 fade_in = region->fade_in()->back()->when; //end is after fade-in, preserve the fade-in
218 if (end > region->last_frame() - region->fade_out()->back()->when)
219 fade_out = region->fade_out()->back()->when - ( region->last_frame() - end ); //end is inside the fadeout, preserve the fades endpoint
225 offset = start - region->position();
226 len = region->length() - offset;
228 if (start < region->last_frame() - region->fade_out()->back()->when) //start is before fade-out, preserve the fadeout
229 fade_out = region->fade_out()->back()->when;
231 if (start < region->position() + region->fade_in()->back()->when)
232 fade_in = region->fade_in()->back()->when - (start - region->position()); //end is inside the fade-in, preserve the fade-in endpoint
236 case OverlapExternal:
237 fade_in = region->fade_in()->back()->when;
238 fade_out = region->fade_out()->back()->when;
242 boost::shared_ptr<AudioRegion> our_region = boost::dynamic_pointer_cast<AudioRegion> (*ours);
245 our_region->set_fade_in_length (fade_in);
246 our_region->set_fade_out_length (fade_out);
252 /* this constructor does NOT notify others (session) */
255 AudioPlaylist::~AudioPlaylist ()
257 _crossfades.clear ();
260 struct RegionSortByLayer {
261 bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
262 return a->layer() < b->layer();
267 AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, framepos_t start,
268 framecnt_t cnt, unsigned chan_n)
270 framecnt_t ret = cnt;
272 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Playlist %1 read @ %2 for %3, channel %4, regions %5 xfades %6\n",
273 name(), start, cnt, chan_n, regions.size(), _crossfades.size()));
275 /* optimizing this memset() away involves a lot of conditionals
276 that may well cause more of a hit due to cache misses
277 and related stuff than just doing this here.
279 it would be great if someone could measure this
282 one way or another, parts of the requested area
283 that are not written to by Region::region_at()
284 for all Regions that cover the area need to be
288 memset (buf, 0, sizeof (Sample) * cnt);
290 /* this function is never called from a realtime thread, so
291 its OK to block (for short intervals).
294 Glib::RecMutex::Lock rm (region_lock);
296 framepos_t const end = start + cnt - 1;
297 framecnt_t read_frames = 0;
298 framecnt_t skip_frames = 0;
299 _read_data_count = 0;
301 _read_data_count = 0;
303 RegionList* rlist = regions_to_read (start, start+cnt);
305 if (rlist->empty()) {
310 map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
311 map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
312 vector<uint32_t> relevant_layers;
314 for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
315 if ((*i)->coverage (start, end) != OverlapNone) {
316 relevant_regions[(*i)->layer()].push_back (*i);
317 relevant_layers.push_back ((*i)->layer());
321 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("Checking %1 xfades\n", _crossfades.size()));
323 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
324 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("%1 check xfade between %2 and %3 ...\n",
325 name(), (*i)->out()->name(), (*i)->in()->name()));
326 if ((*i)->coverage (start, end) != OverlapNone) {
327 relevant_xfades[(*i)->upper_layer()].push_back (*i);
328 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("\t\txfade is relevant, place on layer %1\n",
329 (*i)->upper_layer()));
333 // RegionSortByLayer layer_cmp;
334 // relevant_regions.sort (layer_cmp);
336 /* XXX this whole per-layer approach is a hack that
337 should be removed once Crossfades become
338 CrossfadeRegions and we just grab a list of relevant
339 regions and call read_at() on all of them.
342 sort (relevant_layers.begin(), relevant_layers.end());
344 for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
346 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read for layer %1\n", *l));
348 vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
349 vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
352 for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
353 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
354 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from region %1\n", ar->name()));
356 ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
357 _read_data_count += ar->read_data_count();
360 for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
361 DEBUG_TRACE (DEBUG::AudioPlayback, string_compose ("read from xfade between %1 & %2\n", (*i)->out()->name(), (*i)->in()->name()));
362 (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
364 /* don't JACK up _read_data_count, since its the same data as we just
365 read from the regions, and the OS should handle that for us.
376 AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
378 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
385 fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
390 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
392 if ((*i)->involves (r)) {
393 i = _crossfades.erase (i);
402 AudioPlaylist::flush_notifications (bool from_undo)
404 Playlist::flush_notifications (from_undo);
412 Crossfades::iterator a;
413 for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
414 NewCrossfade (*a); /* EMIT SIGNAL */
417 _pending_xfade_adds.clear ();
423 AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
425 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
426 set<boost::shared_ptr<Crossfade> > updated;
432 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
434 Crossfades::iterator tmp;
439 /* only update them once */
441 if ((*x)->involves (ar)) {
443 pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
446 /* x was successfully inserted into the set, so it has not already been updated */
451 catch (Crossfade::NoCrossfadeHere& err) {
452 // relax, Invalidated during refresh
462 AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
464 boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
465 boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
466 boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
468 for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
469 Crossfades::iterator tmp;
473 boost::shared_ptr<Crossfade> fade;
475 if ((*x)->_in == orig) {
476 if (! (*x)->covers(right->position())) {
477 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
479 // Overlap, the crossfade is copied on the left side of the right region instead
480 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
484 if ((*x)->_out == orig) {
485 if (! (*x)->covers(right->position())) {
486 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
488 // Overlap, the crossfade is copied on the right side of the left region instead
489 fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
494 _crossfades.remove (*x);
495 add_crossfade (fade);
502 AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
504 boost::shared_ptr<AudioRegion> other;
505 boost::shared_ptr<AudioRegion> region;
506 boost::shared_ptr<AudioRegion> top;
507 boost::shared_ptr<AudioRegion> bottom;
508 boost::shared_ptr<Crossfade> xfade;
509 RegionList* touched_regions = 0;
511 if (in_set_state || in_partition) {
515 if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
516 fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
522 refresh_dependents (r);
526 if (!_session.config.get_auto_xfade()) {
530 for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
531 other = boost::dynamic_pointer_cast<AudioRegion> (*i);
533 if (other == region) {
537 if (other->muted() || region->muted()) {
541 if (other->position() == r->position() && other->length() == r->length()) {
542 /* precise overlay of two regions - no xfade */
546 if (other->layer() < region->layer()) {
554 if (!top->opaque()) {
558 OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
560 delete touched_regions;
564 framecnt_t xfade_length;
569 case OverlapInternal:
570 /* {=============== top =============}
571 * [ ----- bottom ------- ]
575 case OverlapExternal:
577 /* [ -------- top ------- ]
578 * {=========== bottom =============}
581 /* to avoid discontinuities at the region boundaries of an internal
582 overlap (this region is completely within another), we create
583 two hidden crossfades at each boundary. this is not dependent
584 on the auto-xfade option, because we require it as basic
588 xfade_length = min ((framecnt_t) 720, top->length());
590 if (top_region_at (top->first_frame()) == top) {
592 xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
593 add_crossfade (xfade);
596 if (top_region_at (top->last_frame() - 1) == top) {
599 only add a fade out if there is no region on top of the end of 'top' (which
603 xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
604 add_crossfade (xfade);
609 /* { ==== top ============ }
610 * [---- bottom -------------------]
613 if (_session.config.get_xfade_model() == FullCrossfade) {
614 touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
615 if (touched_regions->size() <= 2) {
616 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
617 add_crossfade (xfade);
621 touched_regions = regions_touched (top->first_frame(),
622 top->first_frame() + min ((framecnt_t) _session.config.get_short_xfade_seconds() * _session.frame_rate(),
624 if (touched_regions->size() <= 2) {
625 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
626 add_crossfade (xfade);
633 /* [---- top ------------------------]
634 * { ==== bottom ============ }
637 if (_session.config.get_xfade_model() == FullCrossfade) {
639 touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
640 if (touched_regions->size() <= 2) {
641 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
642 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
643 add_crossfade (xfade);
647 touched_regions = regions_touched (bottom->first_frame(),
648 bottom->first_frame() + min ((framecnt_t)_session.config.get_short_xfade_seconds() * _session.frame_rate(),
650 if (touched_regions->size() <= 2) {
651 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, _session.config.get_xfade_model(), _session.config.get_xfades_active()));
652 add_crossfade (xfade);
657 xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
658 _session.config.get_xfade_model(), _session.config.get_xfades_active()));
659 add_crossfade (xfade);
663 catch (failed_constructor& err) {
667 catch (Crossfade::NoCrossfadeHere& err) {
673 delete touched_regions;
677 AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
679 Crossfades::iterator ci;
681 for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
682 if (*(*ci) == *xfade) { // Crossfade::operator==()
687 if (ci != _crossfades.end()) {
688 // it will just go away
690 _crossfades.push_back (xfade);
692 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
693 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
695 notify_crossfade_added (xfade);
699 void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
701 if (g_atomic_int_get(&block_notifications)) {
702 _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
704 NewCrossfade (x); /* EMIT SIGNAL */
709 AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
711 Crossfades::iterator i;
712 boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
714 xfade->in()->resume_fade_in ();
715 xfade->out()->resume_fade_out ();
717 if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
718 _crossfades.erase (i);
723 AudioPlaylist::set_state (const XMLNode& node, int version)
727 XMLNodeConstIterator niter;
731 Playlist::set_state (node, version);
735 nlist = node.children();
737 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
741 if (child->name() != "Crossfade") {
746 boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
747 _crossfades.push_back (xfade);
748 xfade->Invalidated.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_invalidated, this, _1));
749 xfade->PropertyChanged.connect_same_thread (*this, boost::bind (&AudioPlaylist::crossfade_changed, this, _1));
753 catch (failed_constructor& err) {
754 // cout << string_compose (_("could not create crossfade object in playlist %1"),
768 AudioPlaylist::clear (bool with_signals)
770 _crossfades.clear ();
771 Playlist::clear (with_signals);
775 AudioPlaylist::state (bool full_state)
777 XMLNode& node = Playlist::state (full_state);
780 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
781 node.add_child_nocopy ((*i)->get_state());
789 AudioPlaylist::dump () const
791 boost::shared_ptr<Region>r;
792 boost::shared_ptr<Crossfade> x;
794 cerr << "Playlist \"" << _name << "\" " << endl
795 << regions.size() << " regions "
796 << _crossfades.size() << " crossfades"
799 for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
801 cerr << " " << r->name() << " @ " << r << " ["
802 << r->start() << "+" << r->length()
810 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
821 << (x->active() ? "yes" : "no")
827 AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
829 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
835 bool changed = false;
836 Crossfades::iterator c, ctmp;
837 set<boost::shared_ptr<Crossfade> > unique_xfades;
840 RegionLock rlock (this);
842 for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
844 RegionList::iterator tmp = i;
847 if ((*i) == region) {
855 for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
857 set<boost::shared_ptr<Region> >::iterator xtmp = x;
860 if ((*x) == region) {
861 all_regions.erase (x);
868 region->set_playlist (boost::shared_ptr<Playlist>());
871 for (c = _crossfades.begin(); c != _crossfades.end(); ) {
875 if ((*c)->involves (r)) {
876 unique_xfades.insert (*c);
877 _crossfades.erase (c);
884 /* overload this, it normally means "removed", not destroyed */
885 notify_region_removed (region);
892 AudioPlaylist::crossfade_changed (const PropertyChange&)
894 if (in_flush || in_set_state) {
898 /* XXX is there a loop here? can an xfade change not happen
899 due to a playlist change? well, sure activation would
900 be an example. maybe we should check the type of change
904 notify_contents_changed ();
908 AudioPlaylist::region_changed (const PropertyChange& what_changed, boost::shared_ptr<Region> region)
910 if (in_flush || in_set_state) {
914 PropertyChange our_interests;
916 our_interests.add (Properties::fade_in_active);
917 our_interests.add (Properties::fade_out_active);
918 our_interests.add (Properties::scale_amplitude);
919 our_interests.add (Properties::envelope_active);
920 our_interests.add (Properties::envelope);
921 our_interests.add (Properties::fade_in);
922 our_interests.add (Properties::fade_out);
924 bool parent_wants_notify;
926 parent_wants_notify = Playlist::region_changed (what_changed, region);
928 if (parent_wants_notify || (what_changed.contains (our_interests))) {
929 notify_contents_changed ();
936 AudioPlaylist::crossfades_at (framepos_t frame, Crossfades& clist)
938 RegionLock rlock (this);
940 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
941 framepos_t const start = (*i)->position ();
942 framepos_t const end = start + (*i)->overlap_length(); // not length(), important difference
944 if (frame >= start && frame <= end) {
945 clist.push_back (*i);
951 AudioPlaylist::foreach_crossfade (boost::function<void (boost::shared_ptr<Crossfade>)> s)
953 RegionLock rl (this, false);
954 for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
960 AudioPlaylist::update (const CrossfadeListProperty::ChangeRecord& change)
962 for (CrossfadeListProperty::ChangeContainer::const_iterator i = change.added.begin(); i != change.added.end(); ++i) {
966 /* don't remove crossfades here; they will be dealt with by the dependency code */
969 boost::shared_ptr<Crossfade>
970 AudioPlaylist::find_crossfade (const PBD::ID& id) const
972 Crossfades::const_iterator i = _crossfades.begin ();
973 while (i != _crossfades.end() && (*i)->id() != id) {
977 if (i == _crossfades.end()) {
978 return boost::shared_ptr<Crossfade> ();
984 struct crossfade_triple {
985 boost::shared_ptr<Region> old_in;
986 boost::shared_ptr<Region> new_in;
987 boost::shared_ptr<Region> new_out;
991 AudioPlaylist::copy_dependents (const vector<TwoRegions>& old_and_new, Playlist* other) const
993 AudioPlaylist* other_audio = dynamic_cast<AudioPlaylist*>(other);
999 /* our argument is a vector of old and new regions. Each old region
1000 might be participant in a crossfade that is already present. Each new
1001 region is a copy of the old region, present in the other playlist.
1003 our task is to find all the relevant xfades in our playlist (involving
1004 the "old" regions) and place copies of them in the other playlist.
1007 typedef map<boost::shared_ptr<Crossfade>,crossfade_triple> CrossfadeInfo;
1008 CrossfadeInfo crossfade_info;
1010 /* build up a record that links crossfades, old regions and new regions
1013 for (vector<TwoRegions>::const_iterator on = old_and_new.begin(); on != old_and_new.end(); ++on) {
1015 for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
1017 if ((*i)->in() == on->first) {
1019 CrossfadeInfo::iterator cf;
1021 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1023 /* already have a record for the old fade-in region,
1024 so note the new fade-in region
1027 cf->second.new_in = on->second;
1031 /* add a record of this crossfade, keeping an association
1032 with the new fade-in region
1035 crossfade_triple ct;
1037 ct.old_in = on->first;
1038 ct.new_in = on->second;
1040 crossfade_info[*i] = ct;
1043 } else if ((*i)->out() == on->first) {
1045 /* this old region is the fade-out region of this crossfade */
1047 CrossfadeInfo::iterator cf;
1049 if ((cf = crossfade_info.find (*i)) != crossfade_info.end()) {
1051 /* already have a record for this crossfade, so just keep
1052 an association for the new fade out region
1055 cf->second.new_out = on->second;
1059 /* add a record of this crossfade, keeping an association
1060 with the new fade-in region
1063 crossfade_triple ct;
1065 ct.old_in = on->first;
1066 ct.new_out = on->second;
1068 crossfade_info[*i] = ct;
1074 for (CrossfadeInfo::iterator ci = crossfade_info.begin(); ci != crossfade_info.end(); ++ci) {
1076 /* for each crossfade that involves at least two of the old regions,
1077 create a new identical crossfade with the new regions
1080 if (!ci->second.new_in || !ci->second.new_out) {
1084 boost::shared_ptr<Crossfade> new_xfade (new Crossfade (ci->first,
1085 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_in),
1086 boost::dynamic_pointer_cast<AudioRegion>(ci->second.new_out)));
1088 /* add it at the right position - which must be at the start
1089 * of the fade-in region
1092 new_xfade->set_position (ci->second.new_in->position());
1093 other_audio->add_crossfade (new_xfade);
1098 AudioPlaylist::pre_combine (vector<boost::shared_ptr<Region> >& copies)
1100 RegionSortByPosition cmp;
1101 boost::shared_ptr<AudioRegion> ar;
1103 sort (copies.begin(), copies.end(), cmp);
1105 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.front());
1107 /* disable fade in of the first region */
1110 ar->set_fade_in_active (false);
1113 ar = boost::dynamic_pointer_cast<AudioRegion> (copies.back());
1115 /* disable fade out of the last region */
1118 ar->set_fade_out_active (false);
1123 AudioPlaylist::post_combine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1125 RegionSortByPosition cmp;
1126 boost::shared_ptr<AudioRegion> ar;
1127 boost::shared_ptr<AudioRegion> cr;
1129 if ((cr = boost::dynamic_pointer_cast<AudioRegion> (compound_region)) == 0) {
1133 sort (originals.begin(), originals.end(), cmp);
1135 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.front());
1137 /* copy the fade in of the first into the compound region */
1140 cr->set_fade_in (ar->fade_in());
1143 ar = boost::dynamic_pointer_cast<AudioRegion> (originals.back());
1146 /* copy the fade out of the last into the compound region */
1147 cr->set_fade_out (ar->fade_out());
1152 AudioPlaylist::pre_uncombine (vector<boost::shared_ptr<Region> >& originals, boost::shared_ptr<Region> compound_region)
1154 RegionSortByPosition cmp;
1155 boost::shared_ptr<AudioRegion> ar;
1156 boost::shared_ptr<AudioRegion> cr = boost::dynamic_pointer_cast<AudioRegion>(compound_region);
1162 sort (originals.begin(), originals.end(), cmp);
1164 /* no need to call clear_changes() on the originals because that is
1165 * done within Playlist::uncombine ()
1168 for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
1170 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (*i)) == 0) {
1174 /* scale the uncombined regions by any gain setting for the
1178 ar->set_scale_amplitude (ar->scale_amplitude() * cr->scale_amplitude());
1180 if (i == originals.begin()) {
1182 /* copy the compound region's fade in back into the first
1186 if (cr->fade_in()->back()->when <= ar->length()) {
1187 /* don't do this if the fade is longer than the
1190 ar->set_fade_in (cr->fade_in());
1194 } else if (*i == originals.back()) {
1196 /* copy the compound region's fade out back into the last
1200 if (cr->fade_out()->back()->when <= ar->length()) {
1201 /* don't do this if the fade is longer than the
1204 ar->set_fade_out (cr->fade_out());
1209 _session.add_command (new StatefulDiffCommand (*i));